1 条题解

  • 0
    @ 2025-8-24 22:46:31

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar GavinCayne
    绝岭衔延不可攀,我自信步留蹊去。

    搬运于2025-08-24 22:46:31,当前版本为作者最后更新于2023-09-03 01:24:52,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    此篇题解是为补充前两位大佬(均为排名前 200200 的超级大犇)的题解。毕竟人家和本蒟不在同一水平,简单说几句就以为我这样的小蒟蒻能一下搞懂找到门路(嘤嘤嘤)。

    题目大意

    传送门

    给你 nnmm,求 11nn 所有的数异或上 11mm 所有的数结果相加的总和。

    整理一波思路

    1. 暴力计算。直接双环枚举 nnmm,把每一次异或的值算出来再加。这样的话时间复杂度为 O(Tnm)O(Tnm)。由于 n,m1016n,m\le10^{16}必炸
    2. 考虑从异或本身定义出发。只有当 aabb 某一二进制位一个为 11 且另一个为 00 时异或值为 11即当进行到第 nownow 位,若 nn 这边取了一个某位为 11 的数,mm 这边要取一个同一位为 00 的数才能使结果产生贡献。不妨设 nn 这边有 xxnownow 位为 11mm 这边有 yynownow 位为 11,那么 nn 这边贡献(该位为 11 的数的个数)即可表示为 x×(my)x\times(m-y),同理 mm 这边的贡献为 y×(nx)y\times(n-x)nownow 位为 11 时自身数值为 2now2^{now},结果用总贡献乘数值,为 [x×(my)+y×(nx)]×2now[x\times(m-y)+y\times(n-x)]\times2^{now}。然后找到每一位按这种方案求出贡献,加在一起即可。
    3. 如何找到每一位有几个数为 11?(重点来啦,两位奆佬没细讲)设现在在第 nownow 位。那么,不考虑高位,第 nownow 位为 00 的数从 002now12^{now}-1,一共 2now2^{now} 个;而第 nownow 位为 11 的数从 2now2^{now}2now+112^{now+1}-1,也是 2now2^{now} 个。那么我们就可以得出第 now+1now+1 位的数值中第 nownow 位为 11 的占一半!只要我们算出 n÷2now+1n\div2^{now+1} 再乘 2now2^{now} 不就知道这一位有几个数为 11 了吗?(有的人会问这是不是多此一举,直接用 n÷2n\div2 不就得了,但实际上 nownow 可以取到小于等于 nn 的最大偶数次幂,换句话说 2now+12^{now+1} 很有可能超过 nn也就是除法运算时 nn 不够除。)如果 nn 不是 22 的整数次幂,结果必然产生余数。余数中前 2now12^{now}-1 是第 nownow 位为 00(与前文相比排除了 00,即没余数),那么 nmod2now+12now1n\bmod2^{now+1}\le 2^{now}-1 时贡献为 00,反之就减去前 2now12^{now}-1,贡献为 nmod2now+1(2now1)n\bmod2^{now+1}-(2^{now}-1)

    喜闻乐见的代码时间

    为了偷懒,本蒟把 nownow 定义为了 2now2^{now},用 ii 来枚举当前的位置,观看时请务必注意。

    #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;
    }
    

    最后提醒大家时刻不要忘记模 998244353998244353!完结撒花!感谢观看!

    • 1

    信息

    ID
    8098
    时间
    1000ms
    内存
    128MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者