codeforces1119H Triple

190 篇文章 2 订阅
48 篇文章 0 订阅

题面

题意

给出三个数x,y,z,再给出n组数,每组数包含(x+y+z)个数,x个a,y个b,z个c,那么从每一组数中选择一个数的异或值为t的方案数是多少,对每个t输出答案,a,b,c均小于(1<<m).

做法

首先考虑一个简单的暴力,对每组数进行一次fwt,然后全部乘起来,时间复杂度为 O ( 2 m ∗ m ∗ n ) O(2^m*m*n) O(2mmn),肯定不行.
对于一组数a,b,c,可以考虑对数组中的所有位置均异或上a,这样只要最后的数组的所有位置再异或上a,就能使答案不变,而且经过这样的转化后a,b,c就可以看作是0,a^ b,a^c,可以发现,对这组数进行fwt后的数组中只有 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四种权值.
考虑fwt之后相乘,每一位的权值,都可以看作是 ( x + y + z ) c 1 ∗ ( x + y − z ) c 2 ∗ ( x − y + z ) c 3 ∗ ( x − y − z ) c 4 (x+y+z)^{c1}*(x+y-z)^{c2}*(x-y+z)^{c3}*(x-y-z)^{c4} (x+y+z)c1(x+yz)c2(xy+z)c3(xyz)c4显然满足 c 1 + c 2 + c 3 + c 4 = n c1+c2+c3+c4=n c1+c2+c3+c4=n,为了对每一位都求出这四个数,就要考虑构造其他方程.
可以考虑x和y同号的方案数减去x和y异号的方案数,这个值恰好为对数组 A [ a A[a A[a^ b ] b] b]++(每一组数的a,b)做一次fwt后的值,因此对第i位可得 c 1 + c 2 − c 3 − c 4 = A [ i ] c1+c2-c3-c4=A[i] c1+c2c3c4=A[i].
对于x和z,y和z也可如此,这样一共就能得到四个方程,凭此解出 c 1 , c 2 , c 3 , c 4 c1,c2,c3,c4 c1,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]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值