1. 程式人生 > 其它 >【Unknown Source】異或和

【Unknown Source】異或和

Descripition

給定 \(R,n\),求在 \([0,R)\) 中選擇 \(n\) 個互不相同的數字滿足其異或和為 \(0\) 的方案數 \(\mod 998244353\) 的結果

\(R\) 非常大,給出其二進位制下表示中的所有 \(1\) 的位置:共 \(K\) 個從低到高給出的 \(a_i\),其中 \(\max a_i\le 1145141919810\)

\(n,K\le 114514\)

Solution

先求有多少選擇數的排列,最後除掉階乘

考慮一類集合劃分容斥,將所有元素分成若干集合,每個集合內部數字相等,對這些集合劃分的方式賦以一定的係數使得合法者貢獻 \(1\) 次,非法者貢獻 \(0\)

這個模型和 \(\rm ABC236 Ex\) 是一樣的,將所有元素互不相等的關係轉化如下

由於排列中任意兩個元素都可能相等,所以可以將元素自身看做一個點,元素之間的相等關係看做邊

取出全部邊集的一個子集 \(E\),強制每個聯通塊中的元素數值相等,聯通塊之間不強制不相等,並要求所有元素的 \(\oplus\) 和為 \(0\) ,貢獻係數是 \((-1)^{|E|}\),方案數 \(f(E)\) 是給元素分配數值使其滿足相等關係且異或和為 \(0\) 的方案數

關於該容斥係數的正確性

如果圖上不存在邊(也就是所有元素都相等,是一個合法方案)那麼選擇邊表子集的方案數為 \(1\)

否則有元素相等,不合法,需要證明其所有方案的係數之和為 \(0\)

,可以找到圖上存在的一條邊 \(e\),對於所有其它邊的選取方案,選擇 \(e\) 和不選擇 \(e\) 會導致奇偶性不同,係數正負 \(1\) 消去了



注意到 \(f(E)\) 的值只和 \(E\) 中包含的奇數聯通塊的個數有關,那麼所有的 \(f(E)\) 可以歸為對於 \(k\in[0,n]\) 計算選擇 \(k\) 個不要求不等且都 \(\in[0,R)\) 的數字使得異或和為 \(0\)

考慮對於單個 \(k\),列舉這些數字和 \(R\)\(\rm LCP\) 以及這個位置上有多少個數字選了 \(1\)

如果這位選擇了 \(1\),後面選數字的方案是字尾位上權值和 \(s\),選擇 \(0\)

的數字中有一個用來最後進行抵消,另外的數字選擇方案是當前位的權值 \(b\),方案數即:

\[\sum_{i=0}^{k-1}[2|i]\binom{k}{i} s^{i}b^{k-i-1}=\frac1{2b}((b-s)^k+(b+s)^k-s^k(1+(-1)^k)) \]

暴力求解 \(\Theta(nK)\) 個這樣的式子就前功盡棄,由於每位的 \(b,s\) 是固定的,變化的只有 \(k\),嘗試計算 \(\sum\limits_{i\ge0} x^{i}\sum\limits_{j=0}^{K-1}\dfrac{s_j^i}{2b_j}=\sum\limits_{j=0}^{K-1}\dfrac{s_j^i}{2b_j(1-x)}\)

使用分治乘法進行通分得到完整多項式,對 \((b\pm s)^k\) 做一樣的通分即可


要求出來答案,避不開的是對於每個 \(k\in[0,n]\) 求出來有幾個 \(f(E)\) 滿足有 \(k\) 個聯通塊大小為奇數

先考察所有選擇邊將 \(x\) 個點聯通的方式 \(\{E\}\) 中,\((-1)^{|E|}\) 之和 \(h(x)\),注意到 \(h(1)=1\)

\(x>1\) 時,用全部方案減去不連通方案,全部方案的權值和上面提及了是 \(0\) ,不連通則列舉 \(1\) 所在聯通塊的大小

但是在外部點數 \(>1\) 的情況下給它們任意連邊的所有方案權值和是 \(0\)

只有一個點時選點有 \(x-1\) 種方案,每個點對應的權值之和都是 \(h(x-1)\) ,所以有 \(h(x)=-(x-1)h(x-1)\) ,得到 \(h(n)=(-1)^{n-1}(n-1)!\)

那麼設 \(F(x)\) 為選出一個大小為奇數的集合的 \(\rm EGF\),同時設 \(G(x)\) 為所有偶數大小集合的 \(\rm EGF\) ,使用上面所說的容斥係數可以得到下面的式子

\[\begin{aligned}F(x)&=\sum_{i\ge 0} \dfrac{x^{2i+1}}{2i+1}=\dfrac12(\ln(1+x)-\ln(1-x))\\G(x)&=\exp\left(-R\sum_{i\ge 1}\dfrac{x^{2i}}{2i}\right)=(1-x^2)^{R/2}\end{aligned} \]

直接使用 \(F^{-1}(x)\) 表示 \(F(x)\) 的複合逆,根據複合逆定義有:

\[2F(x)=\ln(\dfrac{1+x}{1-x})\\e^{2F(x)}=\dfrac{1+x}{1-x}\\e^{2x}=\frac{1+F^{-1}(x)}{1-F^{-1}(x)}\\F^{-1}(x)=\frac{e^{2x}-1}{e^{2x}+1} \]

\(H(x)=G(F^{-1}(x))\),有 \(H(F(x))=G(x)\)

套用擴充套件拉格朗日反演公式:

\[[x^n]H(F(x))=[x^{n-1}]H'(x)\left(\dfrac{x}{F^{-1}(x)}\right)^n \] \[\begin{aligned}&[x^n]\frac{G}{1-yF}\\ &=[x^{n}]\dfrac{H(F)}{1-yF}\\ &=[x^{n-1}]\dfrac1n\left(\dfrac{H(x)}{1-yx}\right)'\frac{x^n}{F^{-1}(x)^n}\\ &=[x^{n-1}]\dfrac1n\dfrac{H(x)'(1-yx)+yH(x)}{(1-yx)^2}\frac{x^n}{F^{-1}(x)^n}\\ &=[x^{n-1}]\dfrac1n\dfrac{H(x)'+y(H(x)-H'(x)x)}{(1-yx)^2}\frac{x^n}{F^{-1}(x)^n}\\ \end{aligned}\]

由於所需為一個關於 \(y\) 的多項式且每項都有 \(x^n\),那麼需要繼續進行和式變換

\[\begin{aligned}&[x^{n-1}]\frac{x^n(H(x)'+y(H(x)-xH'(x)))}{nF^{-1}(x)^n}\sum_{i\ge 0}(i+1)(yx)^{i}\\ &=[x^{n-1}]\frac{x^nH(x)'}{nF^{-1}(x)^n}+[x^{n-1}]\left(\frac{x^nH(x)'}{nF^{-1}(x)^n}\sum_{i\ge 1}(i+1)(yx)^{i}+\frac{x^n(H(x)-xH'(x))}{nF^{-1}(x)^n}\sum_{i\ge 1}iy^{i}x^{i-1}\right)\\ \\ &=[x^{n-1}]\frac{x^nH(x)'}{nF^{-1}(x)^n}+\sum_{i\ge 1}y^{i}[x^{n-1}]\left(\frac{x^ix^nH(x)'+ix^{i-1}x^nH(x)}{nF^{-1}(x)^n}\right)\\ \\ \end{aligned}\]

求解負指數次方是很困難的,但是注意到 \(F^{-1}(x)\) 的常數項為 \(0\) 所以可以上下同時除以 \(x^{n}\)

一步一步求複雜度就是 \(\Theta(n\log n)\)

注意到在進行 \(F\) 的乘方運算時每個 \(F\) 之間沒有順序,所以要除掉 \(i!\)


最後統計答案直接將兩個部分得到的權值對位乘起來相加即可

Code

#pragma GCC target("avx")
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
const int N=6e5+10;
int fac[N],ifac[N],inv[N];
int n,K,R;
namespace PART1{
	inline poly get_revF(){
		int pw2=1;
		vector<int> vec1,vec2;
		vec1.resize(n+1); vec2.resize(n+1);
		for(int i=0;i<=n;++i){
			vec1[i]=vec2[i]=mul(ifac[i],pw2);
			ckadd(pw2,pw2);
		}
		ckadd(vec1[0],1); ckdel(vec2[0],1);
		vec1=Inv(vec1,n+1);
		vec2=Mul(vec2,vec1);
		vec2.resize(n+1);
		return vec2;
	}
	vector<int> revF;
	inline poly get_H(){
		vector<int> vec=revF;
		vec=Mul(vec,vec);
		vec.resize(n+1);
		for(auto &t:vec) t=del(0,t);
		ckadd(vec[0],1);
		return qpow(vec,mul(R,inv[2]));
	}
	poly main(){
		revF=get_revF();
		vector<int> H=get_H();
		revF.erase(revF.begin());
		revF=qpow(revF,n);
		revF.resize(n+1);
		revF=Inv(revF,n+1);
		vector<int> dH=deriv(H);
		poly M=Mul(H,revF);
		poly dM=Mul(dH,revF);
		M.resize(n+1); dM.resize(n);
		vector<int> ans={dM[n-1]};
		ans.resize(n+1);
		for(int i=1;i<=n;++i){
			int val1=i==n?0:dM[n-1-i];
			int val2=mul(i,M[n-i]);
			ans[i]=add(val1,val2);
			ckmul(ans[i],ifac[i]);
		}
		for(auto &t:ans) ckmul(t,inv[n]);
		return ans;
	}
}
namespace PART2{
	vector<int> A,T;
	inline pair<vector<int>,vector<int> >solve(int l,int r){
		if(l==r)return {{1},{T[l],del(0,mul(A[l],T[l]))}};
		int mid=(l+r)>>1;
		pair<poly,poly> a=solve(l,mid),b=solve(mid+1,r);
		pair<poly,poly> c;
		int lim=1; while(lim<=a.sec.size()+b.sec.size()) lim<<=1;
		NTT(a.sec,lim,1);
		NTT(b.sec,lim,1);
		NTT(a.fir,lim,1);
		NTT(b.fir,lim,1);
		c.fir.resize(lim);
		c.sec.resize(lim);
		for(int i=0;i<lim;++i){
			c.fir[i]=add(mul(a.fir[i],b.sec[i]),mul(b.fir[i],a.sec[i]));
			c.sec[i]=mul(a.sec[i],b.sec[i]);
		}
		NTT(c.fir,lim,-1); NTT(c.sec,lim,-1);
		while(c.fir.size()>1&&!c.fir.back()) c.fir.pop_back();
		while(c.sec.size()>1&&!c.sec.back()) c.sec.pop_back();
		return c;
	}
	int t[N],s[N];
	poly main(){
		for(int i=1;i<=K;++i) s[i]=add(s[i-1],t[i]=ksm(2,a[i]%(mod-1)));
		A.resize(K); T.resize(K);
		for(int i=1;i<=K;++i){
			A[i-1]=del(t[i],s[i-1]);
			T[i-1]=add(t[i],t[i]);
		}
		pair<poly,poly> tmp=solve(0,K-1);
		tmp.sec.resize(n+1);
		vector<int> vec1=Mul(tmp.fir,Inv(tmp.sec,n+1));;

		for(int i=1;i<=K;++i){
			A[i-1]=add(t[i],s[i-1]);
			T[i-1]=add(t[i],t[i]);
		}
		tmp=solve(0,K-1);
		tmp.sec.resize(n+1);
		vector<int> vec2=Mul(tmp.fir,Inv(tmp.sec,n+1));;

		for(int i=1;i<=K;++i){
			A[i-1]=s[i-1];
			T[i-1]=add(t[i],t[i]);
		}
		tmp=solve(0,K-1);
		tmp.sec.resize(n+1);
		vector<int> vec3=Mul(tmp.fir,Inv(tmp.sec,n+1));;
		vec1.resize(n+1); vec2.resize(n+1); vec3.resize(n+1);
		vector<int> ans=Plus(vec2,vec1);
		for(int i=0;i<=n;++i){
			ckdel(ans[i],vec3[i]);
			if(i&1) ckadd(ans[i],vec3[i]);
			else ckdel(ans[i],vec3[i]); 
		}
		for(int i=1;i<=n;i+=2){
			int S=s[K-1],T=t[K];
			int coef=ksm(add(T,T),mod-2);
			int val=add(ksm(del(T,S),i),ksm(add(T,S),i));
			if(!(i&1)) ckadd(val,mul(2,ksm(S,i))); 
			ans[i]=mul(coef,val);
		}
		ans[0]=1;
		return ans;
	}
}
signed main(){
	freopen("xor.in","r",stdin); freopen("xor.out","w",stdout);
	n=6e5; fac[0]=inv[0]=1;
	for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],mod-2);
	for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i),inv[i]=mul(ifac[i],fac[i-1]);
	n=read(); K=read();
	for(int i=1;i<=K;++i)a[i]=read<ll>(),ckadd(R,ksm(2,a[i]%(mod-1)));
	vector<int> res1=PART1::main();
	vector<int> res2=PART2::main();
	int ans=0;
	for(int i=0;i<=n;++i) ckadd(ans,mul(res1[i],res2[i]));
	print(ans);
	return 0;
}