6467. 【GDOI2020模拟02.09】西行寺无余涅槃(FWT的性质)

题目描述

题解

吼题

推荐博客:https://www.cnblogs.com/jz-597/p/12300760.html

最暴力的做法是把n个2^m的FWT乘起来,这样显然不行

先把pi,1~k异或上pi,1,把pi,1变为0,最后再把pi,1异或回去

考虑FWT(xor)的本质,tr(A)=(tr(A1)+tr(A2),tr(A1)-tr(A2)),倒着做可以发现只有变换的u和u+2^k(u&2^k=0)只有u+2^k--->u+2^k这一操作时才需要变号

要求FWT(A)i,就等于把j=0~N-1(N表示FWT的大小,注意大小写)通过不断变号最后加到位置i,变换的实质是把j从后往前一位位变成i

根据上面的结论,只有i和j在当前位都为1时才会变号

记|x|表示x中1的位数,所以(FWT(A)_i=∑_{j=0}^{N-1}{(-1)^{|i&j|}A_j})


n个FWT的每一位只有2^(k-1)种取值(a1必然为正,x&0=0),因此第(I)(注意大小写)位的乘积可以表示成(prod)每种取值^出现次数

考虑求出每个(I)的2^(k-1)种乘积的出现次数,然后求出FWT的积,最后IFWT还原出答案

然而直接求出现次数不好求,考虑用FWT(第二种,不同于上面的)来搞

因为FWT实质上是每一位乘上系数再求和,所以可以把每一位分开求

暴力FWT时在pi,2位置上加上a2,则FWT后位置(I)的a2系数为((-1)^{|I&p_{i,2}|})

在pi,2上+1,FWT后可以得到piFWT后第(I)位的a2系数

若把i=1~n的pi,2都+1,FWT后得到的则是p1~nFWT后第(I)位的a2系数之和,即(I)位所乘的数中a2为正的个数-a2为负的个数

如图,求得的东西就是第(I)位中a2的系数之和,等于正数-负数个数

假设k=3,设x0=a1+a2+a3,x1=a1-a2+a3,x2=a1+a2-a3,x3=a1-a2-a3

那么求得的a2正-a2负=x0+x2-x1-x3

同理,在pi,3处+1可得x0+x1-x2-x3

加上最初的x0+x1+x2+x3=n,一共有3条方程4个未知数,无法解

考虑在pi,2⊕pi,3的位置+1,看看得到什么

首先有一条式子:a&(b⊕c)=(a⊕b)&(a⊕c)

证明:若a的某一位为0,则左右两边都为0,否则都为b⊕c

那么FWT后第(I)位为((-1)^{|I&(p_{i,2} igoplus p_{i,3})|}),由上可知等于((-1)^{|I&(p_{i,2})|}*(-1)^{|I&(p_{i,3})|})

把正看成0,负看成1,乘看成异或,则符号满足位运算的性质,这个东西就是a2与a3的符号的异或,求得的是(异或为正-异或为负)

所以同上可得x0-x1-x2+x3

推广一下,一共有2^(k-1)个未知数,枚举子集异或共有2^(k-1)-1条方程,加上∑xi=n共2^(k-1)条,可以高斯消元

但是消元太慢了,观察一下n=4时FWT的结果

{a,b,c,d}-->{a+b+c+d,a-b+c-d,a+b-c-d,a-b-c+d}

和x的关系完全一致,所以把求得的方程按照顺序(-1看成1,2~k对应二进制从低到高)排好后IFWT(第三种,不同于之前的两种)即可

证明见https://blog.csdn.net/zxyoi_dreamer/article/details/104188934

∑xi=n不必特判,因为当第0位为n时FWT后每一位都为n

注意三种FWT/IFWT的大小与含义差别

code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 998244353
#define two 499122177
#define file
using namespace std;

int P[10]={0,1,2,4,8,16,32,64,128,256};
int p[1000001][10];
long long a[21];
long long A[1048576];
long long b[1048576];
long long c[1048576];
long long p2[1048576];
long long sum[512];
int n,m,m2,K,k2,i,j,k,l,S;

long long qpower(long long a,int b)
{
	long long ans=1;
	
	while (b)
	{
		if (b&1)
		ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	
	return ans;
}

void fwt(int st,long long *a,int N,int len,int type)
{
	int i,j,k,l,s1=2,s2=1,S=N;
	long long u,v;
	
	fo(i,1,len)
	{
		S/=2;
		fo(j,0,S-1)
		{
			fo(k,0,s2-1)
			{
				u=a[st+j*s1+k];
				v=a[st+j*s1+k+s2];
				
				if (type==1)
				a[st+j*s1+k]=(u+v)%mod,a[st+j*s1+k+s2]=(u-v)%mod;
				else
				a[st+j*s1+k]=(u+v)*two%mod,a[st+j*s1+k+s2]=(u-v)*two%mod;
			}
		}
		s1*=2,s2*=2;
	}
}

void dg(int t,int s1,long long s2)
{
	int i;
	
	if (t>K)
	{
		memset(c,0,8*m2);
		sum[s1]=s2;
		
		fo(i,1,n)
		++c[p2[i]];
		fwt(0,c,m2,m,1);
		
		fo(i,0,m2-1)
		b[i*k2+s1]=c[i];
		return;
	}
	
	dg(t+1,s1,(s2+a[t])%mod);
	
	fo(i,1,n) p2[i]^=p[i][t];
	dg(t+1,s1|P[t-1],(s2-a[t])%mod);
	fo(i,1,n) p2[i]^=p[i][t];
}

int main()
{
	freopen("yuyuko.in","r",stdin);
	#ifdef file
	freopen("yuyuko.out","w",stdout);
	#endif
	
	scanf("%d%d%d",&n,&m,&K);m2=qpower(2,m),k2=qpower(2,K-1); //2^(k-1)
	fo(i,1,K)
	scanf("%lld",&a[i]);
	fo(i,1,n)
	{
		fo(j,1,K)
		scanf("%d",&p[i][j]);
		
		S^=p[i][1];
		fd(j,K,1)
		p[i][j]^=p[i][1];
	}
	
//	---
	
	dg(2,0,a[1]); //1
	
	fo(i,0,m2-1) //2
	{
		fwt(i*k2,b,k2,K-1,-1);
		
		A[i]=1;
		fo(j,0,k2-1)
		A[i]=A[i]*qpower(sum[j],b[i*k2+j])%mod;
	}
	
//	---
	
	fwt(0,A,m2,m,-1);
	fo(i,0,m2-1)
	printf("%lld ",(A[i^S]+mod)%mod);
	printf("
");
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/12318077.html