只做了第一题,得分100,有29人得分超过100,zzm140分,只有14个人比他高。这次比赛难度应该是比较大,应该把暴力写好,第三题30分还是比较好拿。第一题写的时间太长了。
A 进制转换
小数转分数是百度搜的规律,应该多想一会。这题耽误很长时间,一直在纠结分数转二进制的时候哪个句子在哪个句子前边。还是思路不清楚的缘故。
还有两个很好的思路。
官方题解:不难发现,我们列方程的方法是将X的小数点向右移动若干位,移出两个正好错开了循环节长度为的小数,此时两个数相减,可以将循环部分消除,便可以解出方程。一般地,假设有p1位非循环位,非循环部分形成的十进制数是k1;有p2位循环节,循环节部分形成的十进制数是k2。
可以得到方程:10^(p1+p2)*X-(10^p1)*X=(10^p2)k1+k2-k1。
比较暴力的做法:直接模拟性价比较高,将十进制的循环部分扩增到1000位
然后不断*2,hash判重。
Codeprogram vijos1062A; var i,a,b,c,t,cnta,tot,ans:longint; pos:array[0..1000000] of longint; s:array[0..1000000] of longint; ch:char; function GCD(a,b:longint):longint; var r:longint; begin repeat r:=a mod b; a:=b;b:=r; until r=0; exit(a); end; begin read(ch);read(ch);read(ch); while ch<>'(' do begin a:=a*10+ord(ch)-ord('0'); inc(cnta);read(ch);end; read(ch);b:=a; while ch<>')' do begin b:=b*10+ord(ch)-ord('0'); c:=c*10+9; read(ch);end; for i:=1 to cnta do c:=c<<1+c<<3; b:=b-a;t:=gcd(b,c); b:=b div t;c:=c div t; tot:=0;ans:=0;tot:=0; //writeln(b,'/',c); while pos[b]=0 do begin inc(tot);pos[b]:=tot;b:=b<<1;ans:=0; if b>=c then begin dec(b,c); ans:=1; end; s[tot]:=ans; end; write('0.'); for i:=1 to pos[b]-1 do write(s[i]); write('('); for i:=pos[b] to tot do write(s[i]); writeln(')'); //for i:=1 to tot do writeln(s[i]:5,pos[i]:5); end.
orzsjx的神模拟
Code program T1062_1; var s:string; n,m,i,j,k,mo,moo:longint; dn,dm,rec:array[0..1000000]of longint; begin readln(s);write('0.'); k:=pos('(',s); mo:=1;moo:=1; for i:=3 to k-1 do mo:=mo*10; for i:=k+1 to length(s)-1 do moo:=moo*10; if k>3 then val(copy(s,3,k-3),n,i); val(copy(s,k+1,length(s)-k-1),m,i); rec[0]:=0; dn[0]:=n; dm[0]:=m; if k>3 then begin for i:=1 to 1000000 do begin rec[i]:=dn[i-1]*2 div mo; dn[i]:=dn[i-1]*2 mod mo+dm[i-1]*2 div moo; dm[i]:=dm[i-1]*2 mod moo+dm[i-1]*2 div moo; for j:=0 to i do if (dn[i]=dn[j])and(dm[i]=dm[j]) then break; if j<>i then begin for k:=1 to j do write(rec[k]); write('('); for k:=j+1 to i do write(rec[k]); writeln(')'); writeln; for k:=0 to i do writeln(rec[k]:5,dn[k]:5,dm[k]:5); halt; end; end; end else begin for i:=1 to 1000000 do begin rec[i]:=dm[i-1]*2 div moo; dm[i]:=dm[i-1]*2 div moo+dm[i-1]*2 mod moo; for j:=0 to i do if dm[i]=dm[j] then break; if i<>j then begin for k:=1 to j do write(rec[k]); write('('); for k:=j+1 to i do write(rec[k]); write(')'); halt; end; end; end; end.
B 时区
好神奇的题目,看了题解以后做的。发现一个人的时区确定了以后他前面的人的时区就有了上界,后面类似,每次找可选时区最少的搜索。
Code VijosNT Mini 2.0.5.7 Special for Vijos 编译通过... ├ 测试数据 01:答案正确... (0ms, 700KB) ├ 测试数据 02:答案正确... (0ms, 700KB) ├ 测试数据 03:答案正确... (0ms, 700KB) ├ 测试数据 04:运行超时... (?, 700KB) ├ 测试数据 05:答案正确... (0ms, 700KB) ├ 测试数据 06:答案正确... (0ms, 700KB) ├ 测试数据 07:答案正确... (0ms, 700KB) ├ 测试数据 08:答案正确... (0ms, 700KB) ├ 测试数据 09:答案正确... (0ms, 700KB) ├ 测试数据 10:答案正确... (0ms, 700KB) program vijos1749; uses math; const FileName='vijos1749';maxn=60; var ans,t:array[0..maxn] of longint; a,n,i:longint; function upint(a,b:longint):longint; begin if a<=0 then exit(0) else exit((a-1)div b+1); end; procedure print; var i:longint; begin for i:=1 to n-1 do write(ans[i],' '); writeln(ans[n]); halt; end; procedure DFS(k:longint;sq,ren:int64); var i,r,l,cnt,find:longint; begin if k>n then print;cnt:=n+1; for i:=1 to n do if(not boolean((ren>>i)and 1)) then begin l:=i;r:=i; while( l>0 )and(not boolean((ren>>l)and 1))do dec(l); while(r<n+1)and(not boolean((ren>>r)and 1))do inc(r); if l<>0 then l:=upint((ans[l]*60+t[l]-t[i]),60)else l:=0; if r<>n+1 then r:= (ans[r]*60+t[r]-t[i])div 60 else r:=n-1; if(r<0)or(l>n-1)or(l>r)then continue; if min(r,n-1)-max(l,0)+1<cnt then begin cnt:=min(r,n-1)-max(l,0)+1; find:=i;end; end; l:=find;r:=find;if k=1 then find:=n>>1;if cnt=n+1 then exit; while( l>0 )and(not boolean((ren>>l)and 1))do dec(l); while(r<n+1)and(not boolean((ren>>r)and 1))do inc(r); if l>0 then l:=upint((ans[l]*60+t[l]-t[find]),60)else l:=0; if r<n+1 then r:=(ans[r]*60+t[r]-t[find]) div 60 else r:=n-1; //writeln(k:5,l:5,r:5,find:5,sq:5,ren:5); for i:=min(r,n-1) downto max(l,0) do if(not boolean((sq>>i)and 1))and(i*60+t[find]<(n-1)*60+59) then begin ans[find]:=i; DFS(k+1,sq or(int64(1)<<i),ren or(int64(1)<<find)); end; end; begin readln(n); for i:=1 to n do begin readln(a); t[i]:=a mod 100+(a div 100)*60 end; DFS(1,0,0); end.
C 建房子
把每一个可能建房屋的左上角作为处理的对象是很重要的一个想法。然后利用单调队列和前缀和可以再平方的复杂度里算出所有的代价。然后用一个双向链表维护可建造的点,暴力删除。
Codeprogram vijos1750; uses math; const maxn=1000;FileName='vijos1750'; type Tqueue=record h,t:longint; QA,QB:array[0..maxn]of longint; end; var Q:array[1..maxn] of TQueue; f:array[1..maxn] of longint; sum:array[0..maxn,0..maxn] of int64; pos,height:array[0..maxn,0..maxn] of longint; cost:array[0..maxn*maxn]of int64; x,y,pre,next:array[0..maxn*maxn] of longint; //forbid:array[1..maxn*maxn] of boolean; nx,ny,i,j,n,m,a,b,tot,s:longint; tmp:TQueue; function cmp(i,j:longint):boolean; begin if cost[i]<cost[j] then exit(true); if cost[i]>cost[j] then exit(false); if x[i]<x[j] then exit(true); if x[i]>x[j] then exit(false); if y[i]<y[j] then exit(true); if y[i]>=y[j] then exit(false); end; procedure swap(var x,y:int64); var t:int64; begin t:=x;x:=y;y:=t;end; procedure swap(var x,y:longint); var t:longint; begin t:=x;x:=y;y:=t;end; procedure Qsort(l,r:longint); var i,j,mid:longint; begin i:=l;j:=r;mid:=(l+r)>>1; x[0]:=x[mid];y[0]:=y[mid];cost[0]:=cost[mid]; repeat while(i<r)and cmp(i,0)do inc(i); while(l<j)and cmp(0,j)do dec(j); if i<=j then begin swap(cost[i],cost[j]); swap(x[i],x[j]); swap(y[i],y[j]); inc(i);dec(j); end; until i>j; if(l<j)then Qsort(l,j); if(i<r)then Qsort(i,r); end; procedure init(var Q:TQueue); begin Q.h:=1;Q.t:=0;end; function Getmin(var Q:TQueue;x:longint):longint; begin with Q do begin while(h<=t)and(QA[h]>x)do inc(h); exit(QB[h]);end; end; procedure ADD(var Q:TQueue;x,y:longint); begin with Q do begin while(h<=t)and(QB[t]>=y)do dec(t); inc(t);QA[t]:=x;QB[t]:=y;end; end; function calc(a,b,c,d:longint):int64; begin exit(sum[c][d]-sum[a-1][d]- sum[c][b-1]+sum[a-1][b-1]);end; begin readln(n,m,a,b); //==getsum== for i:=1 to n do for j:=1 to m do begin read(height[i][j]); sum[i][j]:=sum[i-1][j]+sum[i][j-1] -sum[i-1][j-1]+height[i][j]; end; //==init the Queue== for i:=1 to n do begin init(Q[i]); for j:=m downto m-b+2 do ADD(Q[i],j,height[i][j]); end; //==getminnum== for j:=m-b+1 downto 1 do begin//处理第j列 for i:=1 to n do begin//更新每一列最小值 ADD(Q[i],j,height[i][j]); F[i]:=Getmin(Q[i],j+b-1); end; init(tmp);//初始化队列 for i:=n downto n-a+2 do ADD(tmp,i,F[i]); for i:=n-a+1 downto 1 do//开始算 begin inc(tot); ADD(tmp,i,F[i]); cost[tot]:=Getmin(tmp,i+a-1); x[tot]:=i;y[tot]:=j;end; end; //===getcost=== for i:=1 to tot do cost[i]:=calc(x[i],y[i],x[i]+a-1,y[i]+b-1)-a*b*cost[i]; Qsort(1,tot); for i:=1 to tot do begin pos[x[i]][y[i]]:=i; pre[i]:=i-1; next[i]:=i+1; end; pre[1]:=0;next[tot]:=0;i:=1; //==build== while i<>0 do begin inc(s);pos[x[i]][y[i]]:=0;//没有这句会删掉自己 for nx:=max(x[i]-a+1,1) to min(x[i]+a-1,n)do for ny:=max(y[i]-b+1,1) to min(y[i]+b-1,m)do if pos[nx][ny]<>0 then begin j:=pos[nx][ny]; next[pre[j]]:=next[j]; pre[next[j]]:=pre[j]; pos[nx][ny]:=0;end; i:=next[i];end; //==output== writeln(S);i:=1; while i<>0 do begin writeln(x[i],' ',y[i],' ',cost[i]); i:=next[i];end; end.