异或

https://zybuluo.com/ysner/note/1228125

题面

给出一个大小为\(n\)的非负整数集合\(\{a_1,a_2,...,a_n\}\),等概率地选取\(2^n\)个子集中的一个,设\(S\)为子集中所有数的异或和\(\overline{s_1s_2s_3...s_h}_{(2)}\),求所有情况\((s_1+s_2+s_3+...+s_h)^k\)之和。

  • \(20pts\ n\leq20\)
  • \(60pts\ k=1,2,3,4\)
  • \(100pts\ n\leq100,h\leq63\)

解析

\(20pts\)算法

\(O(2^n)\)暴搜。

\(60pts\)算法

先从\(k=1\)开始吧。
题目转化为求所有情况\(s_1+s_2+s_3+...\)之和。
而每位\(s\)\(1\)(对答案有贡献)决定于子集中该位为\(1\)的数的个数为奇数。
于是我们可以数位分开单独讨论。

假设子集中该位为\(1\)的数的个数为\(tot\)
则选出奇数个的方案数为\(C_{tot}^1+C_{tot}^3+C_{tot}^5...\)
由于该位为\(0\)的数对答案没有影响,可以任选,故方案数还需乘上\(2^{n-tot}\),即为该位对答案的贡献。
所有位贡献相加即可。

处理\(k=4\)需要化式子:

\[\sum(s_1+s_2+s_3+...+s_h)^4 \]

\[=\sum(s_1+s_2+...)*(s_1+s_2+...)*(s_1+s_2+...)*(s_1+s_2+...) \]

\[=\sum\sum_{a=1}^h\sum_{b=1}^h\sum_{c=1}^h\sum_{d=1}^hs_as_bs_cs_d \]

于是枚举\(a,b,c,d\),统计\(s_a,s_b,s_c,s_d\)四位都不为\(0\)的数的个数,组合数处理(当然要取奇数个)即可。
复杂度\(O(h^kn)\)\(k=4\)时会达到\(O(10^9)\)

有没有发现枚举\(a=2,b=1,c=1,d=1\)特别傻逼?这种情况和\(a=1,b=1,c=1,d=2\)一模一样。
我们强制\(a,b,c,d\)不降,然后乘上系数即可。(系数计算机爆算即可)
这样复杂度会除\(4*3*2*1=24\),刚好能过。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define re register
#define il inline
#define ll unsigned long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=300,mod=998244853;
ll n,k,num[N],s[70][70][70][70],C[200][200],jc[200],ans;
il ll gi()
{
  re ll x=0,t=1;
  re char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
il void check(re int *a)
{
	sort(a+1,a+5);
	s[a[1]][a[2]][a[3]][a[4]]++;
}
il void Pre()
{
	re int p[5];
	C[0][0]=1;
	fp(i,1,n)
	{
		C[i][0]=1;
		fp(j,1,n) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	jc[0]=1;fp(i,1,n) jc[i]=(jc[i-1]<<1)%mod;
	fp(a,0,63) fp(b,0,63) fp(c,0,63) fp(d,0,63) p[1]=a,p[2]=b,p[3]=c,p[4]=d,check(p);
}
int main()
{
	n=gi();k=gi();
	fp(i,1,n) num[i]=gi();
	Pre();
	fp(a,0,63)
	fp(b,a,63)
	fp(c,b,63)
	fp(d,c,63)
	{
		re ll tot=0,sum=0;
		fp(i,1,n) 
			if((num[i]&(1ll<<a))&&(num[i]&(1ll<<b))&&(num[i]&(1ll<<c))&&(num[i]&(1ll<<d))) ++tot;
		for(re int i=1;i<=tot;i+=2) (sum+=C[tot][i])%=mod;
		sum=sum*jc[n-tot]%mod*s[a][b][c][d]%mod;
		(ans+=sum)%=mod;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-07-27 16:51  小蒟蒻ysn  阅读(458)  评论(0编辑  收藏  举报