您的位置:首页 > 其它

NOIPの模拟2016_8_11_t1_钱仓

2016-08-11 19:45 141 查看




题目の大意

拿样例来解释一下:

1

0

0

2

0

0

1

2

2

2

从第六个位置开始,每一次遇到零就找离当前位置最近的一个数移过来就可以了

(4位置的2分别移到位置5和位置6,位置1的1移到位置4,位置10的2移到位置2,3。。。以此类推就可以得到正确的答案)~

比赛时の想法:

先要找到第一个值为0的点,然后开始从后面拉东西来填坑(用一个先进先出的队列就可以了),但是我不知道以哪一个点作为开始点,于是就把每一个0的连续块的第一个都做了一遍,愉快的超时~~~~

正确の做法:

上面方法在找开始位置的时候浪费了很多时间,其实我们可以直接找出那个开始点,而不是每一个可能的开始点都跑一遍

先把输入复制一遍,然后定义k为∑x+tmp−1i=xa[i]−tmp 当k>=0时把tmp+1,当k<0时把k的值改为0,并且把tmp的值改为0,意思应该比较明显了,就是当前的串长度为tmp,而在每一个位置都能分配1时还剩余k个点数没有被分配,那么当tmp=n时就说明从当前位置开始做在每一个时刻都不会出现需要的点数比当前拥有的点数多的情况

贴代码

代码还是很简单的3

var
a,f,ex:array[0..200005]of longint;
i,j,k,l,n,y,z,tmp:longint;
bz:boolean;
min:int64;
procedure work(x:longint);
var
i,j,head,ed,cc:longint;
ans:int64;
begin
bz:=false;
ans:=0;
head:=1;
ed:=1;
f[1]:=x;
for i:=x-1 downto x-n+1 do
begin
if a[i]>0 then
begin
cc:=head;
for j:=head to ed do
begin
if a[i]=0 then break;
inc(cc);
dec(a[i]);
if (j=ed) and (a[i]>1) then
begin
bz:=true;
break;
end;
ans:=ans+(f[j]-i)*(f[j]-i);
end;
head:=cc;
if bz=true then break;
end;
if a[i]=0 then
begin
inc(ed);
f[ed]:=i;
end;
end;
if head<=ed then bz:=true;
if bz=false then
if ans<min then min:=ans;
//a:=ex;
end;
begin
//assign(input,'1.in'); reset(input);
readln(n);
for i:=1 to n do readln(a[i]);
for i:=1 to n do a[n+i]:=a[i];
//ex:=a;
min:=maxlongint*10000;
{for i:=1 to n do
if (a[i]=0) and (a[i+1]<>0) then work(i);}
k:=0;
tmp:=0;
for i:=1 to 2*n do
begin
k:=k+a[i]-1;
if k<0 then
begin
k:=0;
tmp:=0;
end else inc(tmp);
if tmp=n then
begin
work(i);
break;
end;
end;
writeln(min);
// close(input);
end.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: