洛谷 P9162

求方差只需要求出 \(\sum_{l=1}^n\sum_{r=l}^{n} f(l,r)\)\(\sum_{l=1}^n\sum_{r=l}^{n} f(l,r)^2\)。因为第一个明显弱于第二个所以只考虑第二个怎么求。

考虑将贡献拆位,并将平方拆成两位的乘积。枚举两位 \(i,j\),设分别 \(b_k,c_k\) 表示 \(a_k\)\(i,j\) 位的值,于是 \(f_{l,r}\) 在第一位为 \(i\) ,第二位为 \(j\) 时的贡献就是

\[((b_i⊕b_{i+1}⊕\cdots ⊕b_j)+(b_i∨b_{i+1}∨\cdots ∨b_j))\times((c_i⊕c_{i+1}⊕\cdots ⊕c_j)+(c_i∨c_{i+1}∨\cdots ∨c_j)) \]

分别对左右两个括号为 \(1/2,1/2\) 的四种情况计数即可。计数的方法可以维护最后一个 \(1\) 的位置和前缀和的奇偶性,这里不懂的可以看代码。

时间 \(O(n\log^2 V)\),空间 \(O(n)\)


#include<bits/stdc++.h>
#define N 1001001
#define MAX 2001
#define inf 1e18
using namespace std;
typedef long long ll;
typedef long double db;
const ll mod=998244353,inv2=(mod+1)/2;
inline void read(ll &ret)
{
    ret=0;char c=getchar();bool pd=false;
    while(!isdigit(c)){pd|=c=='-';c=getchar();}
    while(isdigit(c)){ret=(ret<<1)+(ret<<3)+(c&15);c=getchar();}
    ret=pd?-ret:ret;
    return;
}
ll n,a[N],sum,sum2,b[N],b1[N],b2[N],c[N][2][2];
signed main()
{
	read(n);
	for(int i=1;i<=n;i++)
		read(a[i]);
	for(int k=0;k<=31;k++)//和
	{
		for(int i=1;i<=n;i++)
			b[i]=((a[i]>>k)&1);
		ll res=0;
		ll cnt0=0,cnt1=0;
		cnt0++;
		ll s=0;
		ll las=0;
		for(int r=1;r<=n;r++)
		{
			s+=b[r];
			s&=1;
			if(s==1)
				res+=cnt0,cnt1++;
			else
				res+=cnt1,cnt0++;
			if(b[r])
				las=r;
			res+=las;
			res%=mod;
		}
		sum+=res*((1ll<<k)%mod)%mod;
		sum%=mod;
		sum-=sum>=mod?mod:0;
	}
	for(int k1=0;k1<=31;k1++)//平方和
	{
		for(int k2=0;k2<=31;k2++)
		{
			ll res=0;
			for(int i=1;i<=n;i++)
				b1[i]=(a[i]>>k1)&1,b2[i]=(a[i]>>k2)&1;
			c[0][0][0]=1;
			ll las1=0,las2=0,s1=0,s2=0;
			for(int i=1;i<=n;i++)
			{
				memcpy(c[i],c[i-1],sizeof(c[i]));
				if(b1[i])
					las1=i;
				if(b2[i])
					las2=i;
				s1+=b1[i],s2+=b2[i];
				s1&=1,s2&=1;
				c[i][s1][s2]++;
				if(min(las1,las2)==0)
					continue;
				res+=1*1*(c[min(las1,las2)-1][s1][s2]);
				res+=1*2*(c[min(las1,las2)-1][s1][s2^1]);
				res+=2*1*(c[min(las1,las2)-1][s1^1][s2]);
				res+=2*2*(c[min(las1,las2)-1][s1^1][s2^1]);
				res%=mod;
			}
			sum2+=((1ll<<k1)%mod)*((1ll<<k2)%mod)%mod*res%mod;
			sum2%=mod;
			sum2-=sum2>=mod?mod:0;
		}
	}
	printf("%lld\n",(sum2*n%mod*(n+1)%mod*inv2%mod-sum*sum%mod+mod)%mod);
    exit(0);
}
posted @ 2023-03-22 16:56  CelticOIer  阅读(15)  评论(0编辑  收藏  举报