Codeforces Round 635 Div1 E2(線性基)(FWT)(組合數學)

傳送門

  • orzorz

  • 考慮令線性基 AA 的大小爲 kk,能表示的數爲 S(A)S(A)
    對於簡單版本,若 k217k\le 2^{17} 可以暴力 dfsdfs,否則考慮爲 0 的 mkm-k 位,若之前選了某些數,那麼對這些爲 0 的位有一些貢獻,選爲 1 的位貢獻一定爲 1,那麼我們把這 mkm-k 位壓下來,fi,j,Sf_{i,j,S} 表示到第 ii 位,選了 jj 個,集合爲 SS 的方案數即可

  • HardHard:考慮求第 cc 位的答案,令 ai=[iS(A)],fSc=[popcnt(S)=c]a_i=[i\in S(A)],f_S^c=[popcnt(S)=c] 那麼最後的答案就是 (Afc)0(A\otimes f^c)_0,這個寫成 fwtfwt 的形式就是 ifwt(fwt(A)fwt(fc))0ifwt(fwt(A)*fwt(f^c))_0,這個的意義就是
    SA^Sf^Sc\sum_S\hat A_S*\hat f_S^c,下面來探究這兩個的性質

  • 首先 fScf_S^c 的值只與 popcnt(S)popcnt(S) 有關,強行推導一波
    T(1)ST[popcnt(T)=c]=j=ST,popcnt(T)=c(1)j=j=0k=popcnt(S)(kj)(mkcj)\sum_T(-1)^{|S\cap T|}[popcnt(T)=c]\\=\sum_{j=|S\cap T|,popcnt(T)=c}(-1)^j\\=\sum_{j=0}^{k=popcnt(S)}\binom{k}{j}\binom{m-k}{c-j}
    現在需要知道 popcnt(S)=kA^S\sum_{popcnt(S)=k}\hat A_S
    注意到 AA 的性質,首先線性基中的運算是封閉的
    AA=A2kA\otimes A=A*2^k,所以 A^SA^S=A^S2k\hat A_S*\hat A_S=\hat A_S*2^k, 即 A^S=0 or 2k\hat A_S=0\ or\ 2^k
    又注意到 A^S0\hat A_S\neq 0 當且僅當 SSS(A)S(A) 中任意一個數交爲偶數(考慮 fwtfwt 的意義)
    這個等價於與任何一個基交爲偶數,而且個數恰爲 2mk2^{m-k}(不會證,可以構造)
    強行構造一下,就是爲 0 位的列向量轉置後當前位添加一個 1,交只可能是 0 或是 2
    這個 mkm-k 個任意線性組合均可(xy+zy=(xz)y|x\cap y|+|z\cap y|=|(x\otimes z)\cap y|
    那麼暴力構造出來 dfsdfs,然後處理一下係數即可

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int Mod = 998244353, iv2=(Mod+1)>>1;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a,b); }
void Dec(int &a, int b){ a = dec(a,b); }
void Mul(int &a, int b){ a = mul(a,b); }
int ksm(int a, int b){ int as=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) as=mul(as,a); return as; }
typedef long long ll;
cs int N = 2e5 + 50;
cs int M = 60;
cs int K = 1 << 18;
int n, m, bin[K]; ll a[M];
vector<ll> b; int as[M];
void ins(ll x){
	for(int i=m-1;~i;i--)
	if(x>>i&1){ if(a[i]) x^=a[i]; else{ a[i]=x; b.pb(i); return; } }
}
int clc(ll x){ 
	int as=bin[x&(K-1)]; x>>=18;
	as+=bin[x&(K-1)]; x>>=18; as+=bin[x]; return as;
}
void dfs(int u, ll now){
	if(u==(int)b.size()){ ++as[clc(now)]; return; }
 	dfs(u+1,now^a[b[u]]); dfs(u+1,now);
}
void work_Small(){ dfs(0,0); for(int i=0,coe=ksm(2,n-b.size()); i<=m; i++) cout<<mul(coe,as[i])<<" "; }
vector<ll> S; int ct[M];
void Dfs(int u, ll now){
	if(u==(int)S.size()){ ++ct[clc(now)]; return; }
	Dfs(u+1,now^S[u]); Dfs(u+1,now);
} 
void work_Large(){
	static int C[M][M];
	for(int i=0; i<=m; i++) C[i][0]=1;
	for(int i=1; i<=m; i++) for(int j=1; j<=i; j++) 
	C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	for(int i=m-1;~i;i--) if(a[i])
	for(int j=i+1; j<m; j++) if(a[j]>>i&1) a[j]^=a[i];
	for(int i=0; i<m; i++) if(!a[i]){ ll T=0; 
		a[i]=1ll<<i;
		for(int j=0; j<m; j++) T=(T<<1)+(a[j]>>i&1); S.pb(T);
	} Dfs(0,0); int iv=ksm(iv2,m);
	for(int i=0,mt=mul(iv,ksm(2,n)); i<=m; i++){
		int as=0; for(int k=0; k<=m; k++){
			int coef=0; for(int j=0,coe,u=min(k,i); j<=u; j++)
			coe=mul(C[k][j],C[m-k][i-j]),(j&1)?Dec(coef,coe):Add(coef,coe);
			Add(as,mul(coef,ct[k]));
		} cout<<mul(as,mt)<<" ";
	} 
}
int main(){ 
	#ifdef FSYolanda
	freopen("1.in","r",stdin);
	#endif 
	scanf("%d%d",&n,&m);
	for(int i=0; i<(1<<18); i++) bin[i]=bin[i>>1]+(i&1);
	for(int i=1; i<=n; i++){
		ll x; scanf("%lld",&x); ins(x);
	} if(b.size()<=26) work_Small();
	else work_Large(); return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章