@xiaoziyao
2021-01-25T14:45:12.000000Z
字数 2001
阅读 1066
解题报告
CF341E Candies Game解题报告:
有点没看懂原来的题解在说什么。
首先特判只有零/一个盒子有糖的情况,输出。
对于三个非空盒子,我们可以通过若干次操作,使得操作后的中。这样只要进行足够多次操作,就可以使得某一个盒子的糖数量归零。
设,那么有()。
我们用二进制表示出,然后枚举从到的最高位,对于的第位进行下列操作:
这样,经过若干次操作后,的值会变成,我们对重新排序,那么一定有。
经过若干次操作,我们便可以让某个盒子里的糖数归零。那么我们对非空的三个盒子做若干次操作就可以只剩两个盒子。
然后分析操作次数:最劣的情况下,对于每个盒子都要进行一次这样的操作,而每次操作都会进行若干轮,轮数可以通过类似辗转相除的复杂度证明,发现轮数和执行的操作数是等价的,而很显然对于操作轮需要的最小值的增长速度是快于斐波那契数列的,也就是说轮数是的,而每一轮的操作数为,最大也是的。
因此,该算法的时间复杂度和操作数均为,足够通过本题。
#include<stdio.h>
const int maxn=1005,maxk=405;
int n,zero,anss,p1,p2,p3;
int ans[maxn*maxk][2];
struct candy{
int v,p;
}c[maxn];
inline void chk(candy &a,candy &b){
candy tmp;
if(a.v>b.v)
tmp=a,a=b,b=tmp;
}
inline void move(candy &a,candy &b){
anss++,ans[anss][0]=a.p,ans[anss][1]=b.p;
b.v-=a.v,a.v<<=1;
}
void merge(candy a,candy b,candy c,candy &res1,candy &res2){
chk(a,b),chk(a,c),chk(b,c);
if(a.v==0){
res1=b,res2=c;
return ;
}
int k=b.v/a.v;
while(k){
if(k&1)
move(a,b);
else move(a,c);
k>>=1;
}
merge(a,b,c,res1,res2);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&c[i].v),c[i].p=i;
zero+=c[i].v==0;
}
if(n-zero<2){
puts("-1");
return 0;
}
for(p1=1;p1<=n&&c[p1].v==0;p1++);
for(p2=p1+1;p2<=n&&c[p2].v==0;p2++);
for(p3=p2+1;p3<=n&&c[p3].v==0;p3++);
while(p3<=n){
candy tmp1,tmp2;
merge(c[p1],c[p2],c[p3],tmp1,tmp2);
c[p2]=tmp1,c[p3]=tmp2;
p1=p2,p2=p3;
for(p3++;p3<=n&&c[p3].v==0;p3++);
}
printf("%d\n",anss);
for(int i=1;i<=anss;i++)
printf("%d %d\n",ans[i][0],ans[i][1]);
return 0;
}