[CodeChef(July)221 Div.4 G]Find A, B, C
作者:互联网
做题时间:2022.7.11
\(【题目描述】\)
有三个非负整数 \(A,B,C\) 与一个正整数 \(N(N\leq 2\times 10^5)\) 满足 \(A,B,C\leq N\) ,给定 \(N+1\) 个函数 \(f(0),f(1),...,f(N)\) ,对于 \(\forall i,0\leq i\leq N\) 满足 \(f(i)=(A \oplus i )+(B\oplus i)+(C\oplus i)\) ,求出任意一组满足条件的 \(A,B,C\) (题目保证有解)
\(【输入格式】\)
第一行一个整数 \(T\) 表示数据组数
每组数据第一行一个整数 \(N\)
第二行 \(N+1\) 个整数表示 \(f\)
\(【输出格式】\)
共 \(T\) 行,每行三个整数表示 \(A,B,C\)
\(【考点】\)
位运算
\(【做法】\)
对于两个不同的 \(f(i)\) 和 \(f(j)\) 来说如果 \(i\) 与 \(j\) 在二进制表示下仅有一位相同,那么就可以推出那一位上 \(A,B,C\) 的情况 。
具体而言,若 \(i\) 与 \(j\) 的第 \(k\) 位不相同(假设 \(i_k=1,j_k=0\) ),那么 \(f(j)-f(i)=(A\oplus i)-(A\oplus j)+(B\oplus i)-(B\oplus j)+(C\oplus i)-(C\oplus j)=2^k\times (A_k+B_k+C_k)\)
即:
\[A_k+B_k+C_k=\frac{f(j)-f(i)}{2^k} \]也就是说我们能够知道三个数的第 \(k\) 位的和。 注意:在确定了三者的和后, \(A_k\) , \(B_k\) 与 \(C_k\) 是无所谓谁是1谁是0的 ,因为在运算过程中 \(A,B,C\) 总是一起参与,且位运算不影响进位。
对于 \(A_k,B_k,C_k\) 的第 \(k\) 位而言,如果其中一个是1,那么异或后对 \(A_k+B_k+C_k\) 的贡献为 \(-1\) ,若它是 \(0\) ,那么异或后对 \(A_k+B_k+C_k\) 的贡献为 \(1\) ,所以最后 \(A_k+B_k+C_k\) 的值为 \([-3,-1,1,3]\) ,对应1的个数为 \([3,2,1,0]\)
只要知道所有的 \(0\leq 2^k\leq N\) 的 \(k\) 即可,可以通过 \(f(2^k)\) 与 \(f(0)\) 进行上述计算,得出第 \(k\) 位的结果。
需要注意的是,给 \(A_k,B_k,C_k\) 赋值的时候其中一个可能超过 \(N\) ,因此可以考虑倒序处理,并且每次率先给最小的赋值。
\(【代码】\)
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
const int N=1e5+50;
int f[N],n,T;
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int a[3]={0,0,0};
for(int i=0;i<=n;i++) scanf("%d",&f[i]);
for(int k=18;k>=0;k--){//倒序处理
int i=(1<<k);
if(i>n) continue;
int tt;
sort(a,a+3);//将a,b,c中最小的率先赋值,平均三者大小
//计算(f(i)-f(0))/2^k的值
if(f[i]>f[0]){//大于0
tt=(f[i]-f[0])/i;
if(tt==1) a[0]^=i;
}
else{//小于0
tt=(f[0]-f[i])/i;
if(tt==3) a[0]^=i,a[1]^=i,a[2]^=i;
if(tt==1) a[0]^=i,a[1]^=i;
}
}
printf("%d %d %d\n",a[0],a[1],a[2]);
}
return 0;
}
标签:int,tt,整数,Find,leq,oplus,include,July,Div.4 来源: https://www.cnblogs.com/Unlimited-Chan/p/16467851.html