专栏文章
题解:P1053 [NOIP 2005 提高组] 篝火晚会
P1053题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mip5crdc
- 此快照首次捕获于
- 2025/12/03 06:25 3 个月前
- 此快照最后确认于
- 2025/12/03 06:25 3 个月前
话说这题和贪心、图论有什么关系。
我们首先证明如下结论:如果目标状态和初始状态有 个人位置失配,则一定可以通过 的代价调整回来。
我们考察其中一个失配的同学 ,假设 Ta 最终应该在的位置是 ,现在所在的位置是 。如果有解的情况下,那么 位置上的同学 一定也是失配的(不然就无解了)。假设 同学最终应该在 位置。
- 如果 ,即 两名同学刚好错开,那么我们可以通过一次 的操作使得 个失配的人都回到应该在的位置。
- 如果 ,那么我们继续考察在 位置的同学 ,然后假设 最终应该在的位置 。如果 ,那么我们可以通过一次 的操作 ,通过 的代价使得 个失配的同学回到应该在的位置。
- 如果还不行,继续按照这个规则考察 ,其中 。可以证明 一定是存在的,因为人数是有限的,最多就是 个人全部进来了。可以通过一次 的操作 使得 个失配的同学回到应该在的位置。
这里的证明倒是有一点图论的味道,最终失配的同学按照上述规则可以形成若干个环,而每个环可以通过一次代价为环大小的操作复原。因此证明了所需的结论。
那么现在问题就变成了,究竟最少有多少个人失配。
根据输入,我们可以确定最后环的形态。
环是没有头尾的,当然可以强制定义一个头尾,即强制第 个同学在序列的开头,检查有多少个人失配,但是这样做是 的。
我们还是考虑以 作为序列的开头。把初始链和目标链对应位作减法,将会得到一个数列 。
旋转目标链和旋转初始链是等价的,我们考虑旋转初始链。
由于初始链是 ,因此旋转之后,除了末尾特殊位置之外,其它位置的 值都同样的增大或减小了 。也就是说,如果两个位置 ,它们是适配的,即它们无需做操作,则 的差将保持不变。
我们也知道,在初始链旋转到某一个位置时,如果一个位置的 ,那么这个位置在这个初始链下是适配的,无需做操作的。因此我们可以统计 里的众数出现的次数,然后我们根据上一段的性质可以通过旋转初始链使得众数都变为 ,那么适配的人数不就最多了吗。
因此,一个 的算法就产生了。
注意链的方向对答案是有影响的,需要顺反做两次。
CPP
int Clock[N],Counter[N],ans;
int n,anti[N][2],target[N],init[N];
int main(){
n=read();
for(int i=1;i<=n;i++){
anti[i][0]=read();
anti[i][1]=read();
init[i]=i;
}
target[1]=1;
target[2]=anti[1][0];
for(int i=2;i<n;i++){
if (target[i-1]==anti[target[i]][0])
target[i+1]=anti[target[i]][1];
else if (target[i-1]==anti[target[i]][1])
target[i+1]=anti[target[i]][0];
else{
printf("-1");
return 0;
}//你喜欢人家,人家不喜欢你
}//构造目标链
for(int i=1;i<=n;i++){
Clock[(target[i]-init[i]+n)%n]++;
Counter[(target[i]-init[n-i+1]+n)%n]++;
}
for(int i=0;i<n;i++)
ans=max(ans,max(Clock[i],Counter[i]));
return printf("%d",n-ans)*0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...