【FWT】CF1119H Triple

【题目】
CF
给出三个数 x , y , z x,y,z x,y,z,再给出 n n n组数,每组数包含 ( x + y + z ) (x+y+z) (x+y+z)个数, x x x a a a, y y y b b b, z z z c c c,那么从每一组数中选择一个数的异或值为 t t t的方案数是多少,对每个 t t t输出答案对 998244353 998244353 998244353取模
n ≤ 1 0 5 , k ≤ 2 1 7 , a , b , c &lt; 2 k n\leq 10^5,k\leq 2^17,a,b,c&lt; 2^k n105,k217,a,b,c<2k

【解题思路】
高妙!
一种暴力的方法是,对于第 i i i组,我们令 f i , a i = x , f i , b i = y , f i , c i = z f_{i,a_i}=x,f_{i,b_i}=y,f_{i,c_i}=z fi,ai=x,fi,bi=y,fi,ci=z,其余为 0 0 0,然后 n n n个多项式 FWT \text{FWT} FWT点积再逆变换就可以得到答案了。

要优化这个暴力,由于只有三个值进行 FWT \text{FWT} FWT,那么对于每个位置的贡献情况只有 8 8 8种,仍然不好考虑。
但我们发现,若将序列整体异或 a i a_i ai,即 f i , 0 = x , f i , a i ⊕ b i = y , f i , a i ⊕ c i = z f_{i,0}=x,f_{i,a_i\oplus b_i}=y,f_{i,a_i\oplus c_i}=z fi,0=x,fi,aibi=y,fi,aici=z,现在每一项的系数只有可能是下面四种: x + y + z , x + y − z , x − y + z , x − y − z x+y+z,x+y-z,x-y+z,x-y-z x+y+z,x+yz,xy+z,xyz

考虑每一个位置 i ( 0 ≤ i &lt; 2 k ) i(0\leq i&lt;2^k) i(0i<2k),那么就是上面四个贡献的若干次方的乘积,不妨设次方分别为 a , b , c , d a,b,c,d a,b,c,d
那么我们现在可以得到一个简单的柿子 a + b + c + d = n a+b+c+d=n a+b+c+d=n。考虑再构造出其他的方程使得我们能解出这个东西,于是可以用 x x x y y y同号的方案数减去 x x x y y y异号的方案数,即仅对 f a i ⊕ b i = 1 f_{a_i\oplus b_i}=1 faibi=1进行 FWT \text{FWT} FWT,我们可以得到另一条柿子: a + b − c − d = c n t 1 a+b-c-d=cnt_1 a+bcd=cnt1。同理再分别对 x , z x,z x,z y , z y,z y,z FWT \text{FWT} FWT,就可以得到: a − b + c − d = c n t 2 , a − b − c + d − c n t 3 a-b+c-d=cnt_2,a-b-c+d-cnt_3 ab+cd=cnt2,abc+dcnt3

联立解方程组即可。

复杂度 O ( k ⋅ 2 k ) O(k\cdot 2^k) O(k2k)

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=262333,mod=998244353,inv2=(mod+1)>>1;
int n,k,X,Y,Z,inv4,tmp;
int ans[N],A[N],B[N],C[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}
int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
void up(int &x,int y){x=upm(x+y);}
int mul(int x,int y){return 1ll*x*y%mod;}
int qpow(int x,int y){int res=1;for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);return res;}

void fwtxor(int *a,int n,int op)
{
	for(int i=1;i<n;i<<=1) 
		for(int j=0;j<n;j+=i<<1)
			for(int k=0;k<i;++k)
			{
				ll x=a[j+k],y=a[i+j+k];
				a[j+k]=upm(x+y);a[i+j+k]=upm(x-y);
				if(!~op) a[j+k]=mul(a[j+k],inv2),a[i+j+k]=mul(a[i+j+k],inv2);
			}
}

int main()
{
#ifdef Durant_Lee
	freopen("CF1119H.in","r",stdin);
	freopen("CF1119H.out","w",stdout);
#endif
	n=read();k=read();X=read();Y=read();Z=read();inv4=qpow(4,mod-2);
	for(int i=1;i<=n;++i)
	{
		int a=read(),b=read(),c=read();
		tmp^=a;A[a^b]++;B[a^c]++;C[b^c]++;
	}
	fwtxor(A,1<<k,1);fwtxor(B,1<<k,1);fwtxor(C,1<<k,1);
	//for(int i=0;i<(1<<k);++i) printf("%d ",C[i]); puts("");
	for(int i=0;i<(1<<k);++i)
	{
		//printf("%d %d %d\n",A[i],B[i],C[i]);
		ll a=upm(((ll)n+A[i]+B[i]+C[i])%mod*inv4%mod),b=upm(((ll)n+A[i]-B[i]-C[i])%mod*inv4%mod);
		ll c=upm(((ll)n-A[i]+B[i]-C[i])%mod*inv4%mod),d=upm(((ll)n-A[i]-B[i]+C[i])%mod*inv4%mod);
		//printf("%lld %lld %lld %lld\n",a,b,c,d);
		ans[i]=1ll*qpow(((ll)X+Y+Z)%mod,a)*qpow(((ll)X+Y-Z)%mod,b)%mod*qpow(((ll)X-Y+Z)%mod,c)%mod*qpow(((ll)X-Y-Z)%mod,d)%mod;
		ans[i]=upm(ans[i]);
	}
	fwtxor(ans,1<<k,-1);
	for(int i=0;i<(1<<k);++i) printf("%d ",ans[i^tmp]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值