drive | game | vigenere | blockade | mod | classroom | 总分 |
70 | 10 | 10 | 0 | 60 | 100 | 250 |
Day1状态很不好。
第一题写时思维十分混乱,而且越写越混乱。坐在我旁边那位用C++,很快就敲完了。使我也不知不觉紧张起来,加快速度。草草敲完后就开始做下一题。结果只得了10分,少写了一个条件。
var
c,k:ansistring;
a:array[1..1000]of longint;
procedure main;
var
lc,lk,ik,ic,i,x:longint;
begin
lc:=length(c);
lk:=length(k);
for i:=1 to lk do
if ord(k[i])<ord(‘a’) then k[i]:=chr(ord(k[i])-ord(‘A’)+ord(‘a’));
for i:=1 to lk do
a[i]:=ord(k[i])-ord(‘a’);
ik:=0;
for ic:=1 to lc do
begin
inc(ik);
if ik>lk then ik:=1;
x:=ord(c[ic])-a[ik];
if (x<ord(‘a’))and(ord(c[ic])>=ord(‘a’)) then x:=x+26;//这里应多加一个“(x<ord(‘A’))and(ord(c[ic])>=ord(‘A’))”
write(chr(x));
end;
end;
begin
assign(input,’vigenere.in’);
assign(output,’vigenere.out’);
reset(input);
rewrite(output);
readln(k);
readln(c);
main;
close(input);
close(output);
end.
第二题
考试时根本没想到要用什么高精度,只是爆搜。
结果考试前一分钟时发现爆搜都写错了。
抑郁了一整天。
结果还阴差阳错的过了一个点。
正解:把所有大臣按a*b从小到大排序,高精度计算即可,注意除法的单精度版本其实不难写。。。没必要写高精除高精。
就算想到这样做,自己也打不出来高精除单精。
第三题
这题就直接模拟了。
但也写了好久,写了3KB。
最朴素的算法都有70分。
正解:
对每个i点维护nextA[i],nextB[i]表示i往后A的话去哪里,B的话去哪里。
这个可以用链表来计算,我们把所有数从小到大排序,然后串成一个链表。
从1到n每次找Hi在链表里的前后4个判断一下,然后把Hi从链表里删掉。
然后这样我们再用nextAB[i]表示i往后A走一次B走一次到哪里。
那么可以发现i->nextAB[i]形成了一颗树。
之后可以用树的算法做。
不过最简单的还是预处理next[i][j]表示i往后A和B都走了2^j次到哪里。
然后从大到小判2的每个次幂。
DAY2
这次做得就顺畅很多了。
第一题,实在不会。
我在数学方面的知识为零,看起来NOIP每年都会来一道很裸的数论题。
正解:
拓展欧几里得算法可以立刻求出ax+by=1的解,那么x就是答案。
或者用费马小定理a^(phi(b)-1)也是答案phi(b)是欧拉函数
第二题,这题一开始也没什么思路,就直接模拟了。能得30分。
但我打完第一,第三题后,还剩接近两个小时,不甘心啊。
于是就开始想别的方法。
最后想出来了一个很奇葩的方法。
1.先把所有数据读进来,用树状数组模拟一遍。
2.再用扫一遍树状数组,把爆了的天数记录下来。
3.清零树状数组,再做一遍步骤1,每扫进一个人的要求,就找一遍 步骤2记录下的 天数的课室会不会爆。
这种方法靠RP,就看数据强不强了。
没想到竟全过了。
var
n,m,d,s,t:longint;
a,tr,g:array[0..1000000+1]of longint;
q:array[0..1000000+1,1..3]of longint;
procedure add(i,x:longint);
var
j:longint;
begin
j:=i;
while j<=n do
begin
tr[j]:=tr[j]+x;
j:=j+j and -j;
end;
end;
function find(i:longint):longint;
var
x,j:longint;
begin
x:=0;
j:=i;
while j>0 do
begin
x:=x+tr[j];
j:=j-j and -j;
end;
exit(x);
end;
procedure main2;
var
i,j:longint;
begin
for i:=1 to m do read(q[i,1],q[i,2],q[i,3]);
for i:=1 to m do
begin
add(q[i,2],q[i,1]);
add(q[i,3]+1,-q[i,1]);
end;
fillchar(g,sizeof(g),0);
for i:=1 to n do
if find(i)>a[i] then begin inc(g[0]); g[g[0]]:=i; end;
if g[0]=0 then begin writeln(0); exit; end;
fillchar(tr,sizeof(tr),0);
for i:=1 to m do
begin
add(q[i,2],q[i,1]);
add(q[i,3]+1,-q[i,1]);
for j:=1 to g[0] do
if find(g[j])>a[g[j]] then begin writeln(-1); writeln(i); exit; end;
end;
end;
procedure main;
var
i,j:longint;
begin
for i:=1 to m do
begin
read(d,s,t);
for j:=s to t do
begin
if a[j]<d then begin writeln(-1); writeln(i); exit; end
else a[j]:=a[j]-d;
end;
end;
writeln(0);
end;
procedure init;
var
i:longint;
begin
read(n,m);
for i:=1 to n do read(a[i]);
if (n<=1000)and(m<=1000) then main//直接模拟
else main2;//树状数组
end;
begin
assign(input, ‘classroom.in’);
assign(output,’classroom.out’);
reset(input);
rewrite(output);
init;
close(input);
close(output);
end.
正解:
让我们考虑现在有m个操作,我们先把这些操作的端点离散化一下,之后可以注意到,相邻的两个关键点之间,只有最少的哪天是有用的,那么我们可以把天数压到2m级别。
然后有m个操作,我们先加入前m/2个,然后用部分和判断一下前m/2个会不会跪,会的话就在前m/2个中递归,不然就在后m/2个中递归。由于我们每次可以压天数。复杂度函数就是F(n)=n+F(n/2)=O(n).
我的意思并不是说二分和线段树会T。。。实际上光用C++用scanf读入在我那ZJ的破机子上都要1s+。。。。我二分和线段树都挺快的。。。。不知道会不会卡读入。。。
第三题,想骗分来的。
判断能不能输出(-1),若不能,则输出(0)。
结果一个没中。
数据不厚道。
正解:
3.先二分答案,那么显然每个军队都是要往上走的。
先让他们全往上。
如果某军队走不到根,就放在那里。
不然记录一下他从根的哪个孩子来的,还能走多久。
那么现在我们在根上有一些军队,一个军队有两个属性,一个是从哪个孩子来的,一个是还能走多久。
一个军队能帮助根的还没有被完全覆盖的孩子的条件是要么他是从那个孩子来的(1),要么他能走的路长于那条边(2)。
如果没有条件(1),那么实际上排个序判断就行了。
考虑条件(1),如果一个军队他可以从根再走回他来自的那个根的孩子,那么条件(1)无意义。
如果一个军队走不回他来自的那个根的孩子,那么如果让另一个军队来保护这个孩子,另一个军队能走的必然比该军队多,这是得不偿失的行为,所以直接让该军队保护他来自的那个根的孩子。
然后条件(1)就没有了,就可以按大小排序然后判断了。
第一题才得了10分。
本以为一定与1=无缘了。
没想到线为245。
RP啊!!!!
踩线拿了个1=。
总结:
这次比赛,坐在我旁边用C++的那位。尽管才高一,但比起赛来明显比我要老道得多。
写完一道较难题之后,他总会写一个程序来对拍。
这样准确率就能高很多了。
noip从3个小时提升为3个半小时,时间多了很多。
对于noip来说,只要我能把我会做的题目都做出来,1=是没问题的。
所以,一定要学会对拍。
另外,无论是这次noip,还是期中考试,我都犯了一些“非智力性因素”导致的错误。
这不是偶然,不是因为“考试时马虎了”
而是因为平时太散漫了。
记住:
“平时像考试,考试像平时”