【UOJ#310】【UNR#2】黎明前的巧克力(FWT)


【UOJ#310】【UNR#2】黎明前的巧克力(FWT)

題面

UOJ

題解

把問題轉化一下,變成有多少個異或和為\(0\)的集合,然后這個集合任意拆分就是答案,所以對於一個大小為\(s\)的集合,其貢獻是\(2^s\)
於是我們可以弄出若干個\((1+2x^{a_i})\)這樣子的多項式,然后異或卷積把它們卷起來就是答案。
根據\(FWT\)異或卷積的理論,如果\(i\)位置有一個\(1\),那么\(FWT\)之后對於\(j\)位置的貢獻是\(-1^{pop\_count(i\&j)}\)
於是\(1\)對於所有位置的貢獻都是\(1\)\(2\)對於所有位置的貢獻是\(\pm 2\),所以對於每一個多項式,其\(FWT\)后的結果不是\(-1\)就是\(3\)
但是對於每一個多項式分別\(FWT\)實在是太過浪費,考慮優化這個過程。
因為我們最終要求的只是每個位置上對應的所有值的乘積,每個位置上不是\(-1\)就是\(3\),那么我們假設有\(x\)\(-1\)\(n-x\)\(3\)
然后我們只需要把\(x\)給解出來就行了。
於是對應這兩個值我們要找到一個等式,我們把所有的多項式加起來然后\(FWT\),這樣子第\(i\)位上的值\(f_i\)就是\(n\)個多項式\(FWT\)之后的和。
於是我們有:\((-1)*x+3*(n-x)=f_i\),很容易就可以把\(x\)解出來。
然后\((-1)^x*3^{n-x}\)就是每個位置\(FWT\)乘起來之后的值。
把這個數組求出來之后再\(IFWT\)一遍就可以得到答案了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 998244353
#define inv2 499122177
#define inv4 748683265
#define MAX 1048576
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,a[MAX],pw[MAX];
void FWT(int *P,int opt,int len)
{
	for(int i=1;i<len;i<<=1)
		for(int j=0,p=i<<1;j<len;j+=p)
			for(int k=0;k<i;++k)
			{
				int X=P[j+k],Y=P[i+j+k];
				P[j+k]=(X+Y)%MOD,P[i+j+k]=(X+MOD-Y)%MOD;
				if(opt==-1)P[j+k]=1ll*P[j+k]*inv2%MOD,P[i+j+k]=1ll*P[i+j+k]*inv2%MOD;
			}
}
int main()
{
	n=read();
	pw[0]=1;for(int i=1;i<=n;++i)pw[i]=3ll*pw[i-1]%MOD;
	for(int i=1;i<=n;++i)a[0]+=1,a[read()]+=2;
	pw[0]=1;
	FWT(a,1,1048576);
	for(int i=0;i<1048576;++i)
	{
		int p=1ll*(n+n+n+MOD-a[i])*inv4%MOD;
		a[i]=(p&1)?(MOD-pw[n-p])%MOD:pw[n-p];
	}
	FWT(a,-1,1048576);
	int ans=(a[0]+MOD-1)%MOD;
	printf("%d\n",ans);
	return 0;	
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM