【THUPC2019】找树 / findtree(FWT)(矩阵树)

传送门


题解:

送分题。

首先求生成树肯定是矩阵树。

然后我们发现这里的卷积全部都是位运算。

众所周知位运算卷积的DFT可以直接考虑每一位的操作,理解的话其实就是每位表示一个维度,然后各个维度上进行各自的DFT即可。

该怎么搞怎么搞。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=998244353,iv2=(mod+1)/2;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int po(int a,int b){int r=1;for(;b;b>>=1,Mul(a,a))if(b&1)Mul(r,a);return r;}
inline void ex_gcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}inline int Inv(int a){int x,y;ex_gcd(mod,a,y,x);return x+(x>>31&mod);} 

cs int N=77,M=1<<12|7;

int n,m,s;
int a[N][N][M],b[M],A[N][N];

char o[100];

void DFT(int *A){
	for(int re i=1,d=0;i<(1<<s);i<<=1,++d)
		for(int re j=0;j<(1<<s);j+=i<<1)
		for(int re k=0;k<i;++k){
			switch(o[d]){
				case '&':Inc(A[j+k],A[i+j+k]);break;
				case '|':Inc(A[i+j+k],A[j+k]);break;
				case '^':{
					int x=A[j+k],y=A[i+j+k];
					A[j+k]=add(x,y);
					A[i+j+k]=dec(x,y);
					break;
				}
			}
		}
}

void IDFT(int *A){
	for(int re i=1,d=0;i<(1<<s);i<<=1,++d)
		for(int re j=0;j<(1<<s);j+=i<<1)
		for(int re k=0;k<i;++k){
			switch(o[d]){
				case '&':Dec(A[j+k],A[i+j+k]);break;
				case '|':Dec(A[i+j+k],A[j+k]);break;
				case '^':{
					int x=A[j+k],y=A[i+j+k];
					A[j+k]=mul(iv2,add(x,y));
					A[i+j+k]=mul(iv2,dec(x,y));
					break;
				}
			}
		}
}

int det(){
	int ans=1;
	for(int re i=1;i<n;++i){
		int p=i;for(;p<n&&!A[p][i];++p);
		if(p==n)return 0;
		if(p!=i){
			ans=mod-ans;
			for(int re k=i;k<n;++k)
				std::swap(A[i][k],A[p][k]);
		}Mul(ans,A[i][i]),p=Inv(A[i][i]);
		for(int re k=i;k<n;++k)Mul(A[i][k],p);
		for(int re j=i+1;j<n;++j)if(int t=A[j][i])
			for(int re k=i;k<n;++k)
				Dec(A[j][k],mul(t,A[i][k]));
	}return ans;
}

void Main(){
	scanf("%d%d%s",&n,&m,o);s=strlen(o);
	for(int re i=1;i<=m;++i){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		Dec(a[u][v][w],1);
		Dec(a[v][u][w],1);
		Inc(a[u][u][w],1);
		Inc(a[v][v][w],1);
	}for(int re i=1;i<n;++i)
		for(int re j=1;j<n;++j)
			DFT(a[i][j]);
	for(int re i=0;i<(1<<s);++i){
		for(int re j=1;j<n;++j)
			for(int re k=1;k<n;++k)
				A[j][k]=a[j][k][i];
		b[i]=det();
	}IDFT(b);int ans=(1<<s)-1;
	while(~ans&&!b[ans])--ans;
	cout<<ans<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("findtree.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值