[THUPC2019] 找树

一、题目

点此看题

二、解法

这道题很离谱啊,看上去是求一个最大值,其实是把生成树权值为 \(i\) 的个数都给算出来,因为权值很小。

既然是生成树可以考虑矩阵树定理,我们考虑他是求这样一个式子:

\[\sum\prod e_{w_i} \]

对于这个乘法的理解是很灵活的,只要他能满足直接点值相乘就可以了,比如把边权换成一次多项式来做加法(就是 \(2020\) 年的联合省选题),而这道题是 \(\tt FWT\) 后做矩阵树定理,因为它满足点值相乘。

具体说来,也就是先求出只考虑边权为 \(v\) 的边的矩阵 \(mp[i][j][v]\) ,然后对每个 \(mp[i][j]\)\(\tt FWT\) ,这里的 \(\tt FWT\) 要魔改一下,因为是每一位是独立的,所以每一位做一种 \(\tt FWT\) 是可以的(比如这一位是或运算就做或 \(\tt FWT\)

现在就满足点值相乘了,我们求出每个边权的行列式 \(c[v]\) ,然后对 \(c\) 做同样的 \(\tt IFWT\) ,就得到了每个边权的方案数了。注意我们要一直让所有权值为正才行。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MOD = 1e9+7;
const int N = 75;
const int M = 4100;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,w,lim,inv,a[N][N][M],r[N][N],c[M];char s[M];
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return r;
}
void fwt(int *a,int n,int op)
{
	for(int i=1,b=0;i<n;i<<=1,b++)
	{
		int p=i<<1;
		if(s[b]=='|')
		{
			for(int j=0;j<n;j+=p)
				for(int k=0;k<i;k++)
				{
					if(op==1) a[i+j+k]=(a[i+j+k]+a[j+k])%MOD;
					else a[i+j+k]=(a[i+j+k]-a[j+k]+MOD)%MOD;
				}
		}
		if(s[b]=='&')
		{
			for(int j=0;j<n;j+=p)
				for(int k=0;k<i;k++)
				{
					if(op==1) a[j+k]=(a[i+j+k]+a[j+k])%MOD;
					else a[j+k]=(a[j+k]-a[i+j+k]+MOD)%MOD;
				}
		}
		if(s[b]=='^')
		{
			for(int j=0;j<n;j+=p)
				for(int k=0;k<i;k++)
				{
					int x=a[j+k],y=a[i+j+k];
					a[j+k]=(x+y)%MOD;
					a[i+j+k]=(x-y+MOD)%MOD;
					if(op==-1)
					{
						a[j+k]=a[j+k]*inv%MOD;
						a[i+j+k]=a[i+j+k]*inv%MOD; 
					}
				}
		}
	}
}
int zy()
{
	int ans=1;
	for(int i=1;i<n;i++)
	{
		for(int j=i+1;j<n;j++)
			if(!r[i][i] && r[j][i])
			{
				ans=MOD-ans;//手误了 
				swap(r[i],r[j]);
				break;
			}
		if(!r[i][i]) return 0;
		int iv=qkpow(r[i][i],MOD-2);
		for(int j=i+1;j<n;j++)
		{
			int t=r[j][i]*iv%MOD;
			for(int k=i;k<n;k++)
				r[j][k]=(r[j][k]-t*r[i][k]%MOD+MOD)%MOD;
		}
		ans=ans*r[i][i]%MOD;
	}
	return ans;
}
signed main()
{
	n=read();m=read();inv=(MOD+1)/2;
	scanf("%s",s);w=strlen(s);lim=1<<w;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),c=read();
		a[u][v][c]=(a[u][v][c]+MOD-1)%MOD;
		a[v][u][c]=(a[v][u][c]+MOD-1)%MOD;
		a[u][u][c]++;a[v][v][c]++;
	}
	for(int i=1;i<n;i++)
		for(int j=1;j<n;j++)
			fwt(a[i][j],lim,1);
	for(int i=0;i<lim;i++)
	{
		memset(r,0,sizeof r);
		for(int j=1;j<n;j++)
			for(int k=1;k<n;k++)
				r[j][k]=a[j][k][i];
		c[i]=zy();
	}
	fwt(c,lim,-1);
	for(int i=lim-1;i>=0;i--)
		if(c[i]>0)
		{
			printf("%lld\n",i);
			return 0;
		}
	puts("-1");
}
posted @ 2021-02-03 22:19  C202044zxy  阅读(115)  评论(0编辑  收藏  举报