codeforces1119H Triple

題面

題意

給出三個數x,y,z,再給出n組數,每組數包含(x+y+z)個數,x個a,y個b,z個c,那麼從每一組數中選擇一個數的異或值爲t的方案數是多少,對每個t輸出答案,a,b,c均小於(1<<m).

做法

首先考慮一個簡單的暴力,對每組數進行一次fwt,然後全部乘起來,時間複雜度爲O(2mmn)O(2^m*m*n),肯定不行.
對於一組數a,b,c,可以考慮對數組中的所有位置均異或上a,這樣只要最後的數組的所有位置再異或上a,就能使答案不變,而且經過這樣的轉化後a,b,c就可以看作是0,a^ b,a^c,可以發現,對這組數進行fwt後的數組中只有x+y+z,x+yz,xy+z,xyzx+y+z,x+y-z,x-y+z,x-y-z四種權值.
考慮fwt之後相乘,每一位的權值,都可以看作是(x+y+z)c1(x+yz)c2(xy+z)c3(xyz)c4(x+y+z)^{c1}*(x+y-z)^{c2}*(x-y+z)^{c3}*(x-y-z)^{c4}顯然滿足c1+c2+c3+c4=nc1+c2+c3+c4=n,爲了對每一位都求出這四個數,就要考慮構造其他方程.
可以考慮x和y同號的方案數減去x和y異號的方案數,這個值恰好爲對數組A[aA[a^b]b]++(每一組數的a,b)做一次fwt後的值,因此對第i位可得c1+c2c3c4=A[i]c1+c2-c3-c4=A[i].
對於x和z,y和z也可如此,這樣一共就能得到四個方程,憑此解出c1,c2,c3,c4c1,c2,c3,c4即可.

代碼

#include<bits/stdc++.h>
#define ll long long
#define N 150000
#define M 998244353
using namespace std;

ll n,m,x,y,z,er,si,tmp,A[N],B[N],C[N],ans[N];

inline ll po(ll u,ll v)
{
	ll res=1;
	for(;v;)
	{
		if(v&1) res=res*u%M;
		u=u*u%M;
		v>>=1;
	}
	return res;
}

inline void fwt(ll *a,bool dft)
{
	ll i,j,k,x,y;
	for(i=1;i<(1 << m);i<<=1)
	{
		for(j=0;j<(1 << m);j+=(i<<1))
		{
			for(k=j;k<i+j;k++)
			{
				x=a[k],y=a[k+i];
				a[k]=(x+y)%M;
				a[k+i]=(M+x-y)%M;
				if(!dft)
				{
					a[k]=a[k]*er%M;
					a[k+i]=a[k+i]*er%M;
				}
			}
		}
	}
}

int main()
{
	er=(M+1)/2,si=po(4,M-2);
	ll i,j,a,b,c,d;
	cin>>n>>m>>x>>y>>z;
	for(i=1;i<=n;i++)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		tmp^=a,A[a^b]++,B[a^c]++,C[b^c]++;
	}
	fwt(A,1),fwt(B,1),fwt(C,1);
	for(i=0;i<(1 << m);i++)
	{
		a=(n+A[i]+B[i]+C[i])*si%M;
		b=(n+A[i]-B[i]-C[i])*si%M;
		c=(n-A[i]+B[i]-C[i])*si%M;
		d=(n-A[i]-B[i]+C[i])*si%M;
		a=(a+M)%M,b=(b+M)%M,c=(c+M)%M,d=(d+M)%M;
		ans[i]=po(x+y+z,a)*po(x+y-z,b)%M*po(x-y+z,c)%M*po(x-y-z,d)%M;
		ans[i]=(ans[i]+M)%M;
	}
	fwt(ans,0);
	for(i=0;i<(1 << m);i++) printf("%lld ",ans[i^tmp]);
}

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