P5390 [Cnoi2019]數學作業(位運算&組合數學)

P5390 [Cnoi2019]數學作業(位運算&組合數學)

傳送門

題意:求給定集合所有子集元素異或和的求和。

ans=siSxor ai,aisians=\sum\limits_{s_i\subset S}xor\ a_i,a_i\in s_i

思路:考慮每個位上的貢獻次數。

設對於集合SS,當前位上爲11的個數爲xx,顯然我們需要選出奇數個11才能使該位異或爲11,其他爲00的數可選可不選,方案數爲:2nx2^{n-x},顯然對於xx個1,選出奇數個1和偶數個11方案是一樣的,因爲對於xx爲奇數,(0,2,,x1)=(1,3,,x)|(0,2,\dots,x-1)|=|(1,3,\dots,x)|.

偶數同理。

所以選出奇數個方案數爲2x12^{x-1}.

所以對於第kk位的貢獻是:2k×2nx×2x1=2k×2n12^k\times 2^{n-x}\times 2^{x-1}=2^k\times 2^{n-1}.

需要注意的是當x=0x=0時是不可能產生貢獻的,所以我們只需要將集合SS中所有數進行按位或運算,再乘上2n12^{n-1}即可。

時間複雜度:O(nlogn)O(nlogn)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=998244353;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
inline void read(int &x){ 
	x=0;int w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
	for(;ch>='0'&&ch<='9';ch=getchar())
		x=(x<<3)+(x<<1)+(ch&15);
	x*=w; 
}
ll ksm(ll a,ll n){
	ll ans=1;
	while(n){
		if(n&1) ans=ans*a%mod;
		a=a*a%mod;
		n>>=1;
	}
	return ans;
}
int main(){
	int  t;
	read(t);
	while(t--){
		int n;
		read(n);
		int ans=0;
		for(reg int i=1;i<=n;i++){
			int x;
			read(x);
			ans=ans|x;
		}
		ans=1LL*ans*ksm(2,n-1)%mod;
		printf("%d\n",ans);
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章