1 条题解
-
0
自动搬运
来自洛谷,原作者为

GavinCayne
绝岭衔延不可攀,我自信步留蹊去。搬运于
2025-08-24 22:42:53,当前版本为作者最后更新于2023-08-04 01:15:28,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P8826 游戏 题解
这是当年传智杯初赛最难的题,说是黄题中的佼佼者也不为过。甚至过了三年提交 通过人数竟不满 !而且无一篇题解(
被我捡漏)!一开始本蒟蒻也没有思路,后来看了讨论区别的大佬的思路才豁然开朗(此题数据有问题)。题目大意
给定 个数,两种操作:每次选 和 ,若 的值二进制只有 位为 ,则代价 需要加上 ,如果大于 位则代价 加上 。做完操作删去其中一个数,问剩最后一个数时 最小是多少。
整理一波思路
- 若 无论异或情况如何代价都不变。直接用操作次数 乘上一次代价 出结果。
- 一位一位判断异或结果实在麻烦,需要转成二进制再倒着(短除法性质)判断每一位上的值是不是 ,要循环两次。有什么方法一下就能知道异或的值是不是 位是 呢?用 函数! 表示 最低位为 的数和它后面的数构成的数值。例: 表示 最低位为 的数及它后面的 构成的数 。那么当 时, 一定是 的整数次幂(除最高位全是 ),它的二进制为 的位只可能是最高位!
- 如何写 ?假设 最低位的 在第 位上,那么 取反后 以后所有的位数全是 , 位为 。 是正数,取反后变成负数,补码要加 ,刚好 位又回到 。那么 与取反后的 做与运算(常识:与运算是将两个数的补码运算)由于 位前面的数全由于取反导致与的值为 ,所以结果就是 位为 其它全是 。即 可写作 。
- 处理好判断异或值了,接下来就要考虑正式操作。统计两种操作中代价较小的次数为 ,则代价大的操作次数为 。用两者分别乘上每次的代价 和 就能得出正确结果!
- 考虑并查集(是的,你没听错,这题还与并查集挂钩)。不难理解,操作不会改变数本身的大小,只需要删掉即可。那么把操作过的数并在一个集合里,每操作一次判断操作的两个数在不在同一集合,不在则并在一起,同时操作次数加一。
喜闻乐见的代码时间
#include<bits/stdc++.h> using namespace std; #define int long long const int M=1e4+5; int n,c1,c2,a[M],ans=0,f[M]; int findfather(int x)//并查集标准模板:找爹 { if(f[x]!=x)f[x]=findfather(f[x]); return f[x]; } int lowbit(int x)//找最低的值为1的位 { return x&(-x); } signed main() { cin>>n>>c1>>c2; for(int i=1;i<=n;i++) { cin>>a[i];f[i]=i; } sort(a+1,a+n+1);//可不排 if(c1==c2)//判断,如果相等则怎么操作代价都一样 { cout<<(n-1)*c1; return 0; } for(int i=1;i<n;i++) { for(int j=i+1;j<=n;j++) { int pd=0,xornum=a[i]^a[j]; if((xornum||n<=10)&&lowbit(xornum)==xornum)pd=1;//xornum表示异或值不为0,也就是不等 if((c1<c2&&pd)||(c1>c2&&!pd))//C1划算就要异或值只有一位为1,也就是pd成立 { int f1=findfather(i),f2=findfather(j);//并查集 if(f1!=f2) { ans++;f[f1]=f2; } } } } cout<<(max(c1,c2)*(n-1-ans)+min(c1,c2)*ans);//简单乘法 return 0; } //最难的黄题搞定了!!!参考资料: 详解。 参考 ShanireZ 大佬的做法。 大佬的讨论 同时也是判断异或中 的由来(数据的锅,不能保证数据正确)。完结撒花~
- 1
信息
- ID
- 6322
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者