P9223 异或 题解
GavinCayne · · 题解
此篇题解是为补充前两位大佬(均为排名前 嘤嘤嘤)。
题目大意
传送门
给你
整理一波思路
- 暴力计算。直接双环枚举
n 和m ,把每一次异或的值算出来再加。这样的话时间复杂度为O(Tnm) 。由于n,m\le10^{16} ,必炸。 - 考虑从异或本身定义出发。只有当
a 和b 某一二进制位一个为1 且另一个为0 时异或值为1 。即当进行到第now 位,若n 这边取了一个某位为1 的数,m 这边要取一个同一位为0 的数才能使结果产生贡献。不妨设n 这边有x 个now 位为1 ,m 这边有y 个now 位为1 ,那么n 这边贡献(该位为1 的数的个数)即可表示为x\times(m-y) ,同理m 这边的贡献为y\times(n-x) 。now 位为1 时自身数值为2^{now} ,结果用总贡献乘数值,为[x\times(m-y)+y\times(n-x)]\times2^{now} 。然后找到每一位按这种方案求出贡献,加在一起即可。 - 如何找到每一位有几个数为
1 ?(重点来啦,两位奆佬没细讲)设现在在第now 位。那么,不考虑高位,第now 位为0 的数从0 到2^{now}-1 ,一共2^{now} 个;而第now 位为1 的数从2^{now} 到2^{now+1}-1 ,也是2^{now} 个。那么我们就可以得出第now+1 位的数值中第now 位为1 的占一半!只要我们算出n\div2^{now+1} 再乘2^{now} 不就知道这一位有几个数为1 了吗?(有的人会问这是不是多此一举,直接用n\div2 不就得了,但实际上now 可以取到小于等于n 的最大偶数次幂,换句话说2^{now+1} 很有可能超过n ,也就是除法运算时n 不够除。)如果n 不是2 的整数次幂,结果必然产生余数。余数中前2^{now}-1 是第now 位为0 (与前文相比排除了0 ,即没余数),那么n\bmod2^{now+1}\le 2^{now}-1 时贡献为0 ,反之就减去前2^{now}-1 ,贡献为n\bmod2^{now+1}-(2^{now}-1) 。
喜闻乐见的代码时间
为了偷懒,本蒟把
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M=998244353;
int t,cnt[33][3];
int times(int x,int y)
{
return ((x%M)*(y%M))%M;
}
int pl(int x,int y)
{
return ((x%M+y%M)%M+M)%M;
}
inline int read()
{
int f=1,k=0;
char c=getchar();//读入一个字符
//非数字
while(c<'0'||c>'9')//读到空格后
{
if(c=='-')f=-1;//读到负数
c=getchar();//两个功能:读取负号后面的数字或者读入空格等。
}
//数字
while(c>='0'&&c<='9')
{
k=(k<<1)+(k<<3)+(c^48);
c=getchar();//一位一位读入数字
}
return f*k;
}
signed main()
{
t=read();
while(t--)
{
int n=read(),m=read(),ans=0,now=1;
memset(cnt,0,sizeof(cnt));
for(register int i=0;(1ll<<i)<=max(n,m);i++)
{
int f=now<<1;
cnt[i][1]=(now<=n?(now*(n/f)+(n%f+1>=now?(n%f-now+1):0)):0);//1~n第i位几个数值位1
cnt[i][2]=(now<=m?(now*(m/f)+(m%f+1>=now?(m%f-now+1):0)):0);//1~m第i位几个数值位1
ans=pl(ans,times(now,pl(times(cnt[i][1],(m-cnt[i][2])),times(cnt[i][2],n-cnt[i][1]))));
now<<=1;
}
cout<<ans<<endl;
}
return 0;
}
最后提醒大家时刻不要忘记模