test5.11

T1:一道很妙的综合题

首先显然按位做

然后边权只有0/1/2

首先我们把一棵生成树的权值定义为边权之积

(边权可以看做有这么多条重边)

那么我们就可以用矩阵树定理求出所有生成树的权值的积

矩阵树定理就是用来解决生成树计数的

先去自环

构造如下矩阵

g[i][i]=i的度数

如果g[i][j]有一条边,那么g[i][j]=-1

求出它的行列式就是生成树数量


但是我们要求的是边权和

那么我们把边权看做一个多项式

乘起来就可以达到指数相加的效果了

把边权看做多项式的指数

构造出对应的矩阵,其中每个元素都是多项式

用矩阵树定理就可以求出答案多项式

系数即为权值和为指数的方案数

但是这样我们就要写一堆奇怪的多项式运算,复杂度O(n^5*log)

(你可以用FFT)

FFT并没有什么卵用


我们发现生成树的边权和最大为2*(n-1)

于是我们就知道了答案多项式的最高次

我们用点值表示,用矩阵树定理求出0-2*n的点带入后的值

然后拉格朗日插值法求出答案多项式即可


于是复杂度就只有O(n^4*log)

我的插值写成O(n^3)了虽然这题没什么关系

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
const ll maxn=105,maxm=maxn*maxn,mod=1e9+7;

using namespace std;
struct Edge{int u,v,l;}E[maxm];
int n,m;ll g[maxn][maxn],ans,down[maxn];
struct poly{
	ll a[maxn],len;
	void clear(){memset(a,0,sizeof(a)),len=0,a[0]=1;}
	ll &operator [] (const ll &x){return a[x];}
	void watch(){
		for (ll i=len;i>=0;i--) printf("%d ",a[i]);puts("");
	}
	poly operator *(poly b){
		poly res;res.len=len+b.len;
		memset(res.a,0,sizeof(res.a));
		for (ll i=0;i<=len;i++)
			for (ll j=0;j<=b.len;j++)
				res[i+j]=(res[i+j]+1ll*a[i]*b.a[j]%mod)%mod;
		return res;
	}
}ori,f,pre[maxn],h,back[maxn];
void read(ll &x){
	char ch;
	for (ch=getchar();!isdigit(ch);ch=getchar());
	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}

ll qpow(ll a,ll b){
	ll res=1;
	for (;b;b>>=1,a=1ll*a*a%mod)
		if (b&1) res=1ll*res*a%mod;
	return res;
}

ll inv(ll x){
	return qpow(x,mod-2);
}

ll det(ll n){
	ll res=1;
	for (ll i=1,j;i<=n;i++){
		for (j=i;j<=n&&!g[j][i];j++);
		if (j>n) return 0;
		if (j!=i){
			res*=-1;
			for (ll k=1;k<=n;k++)
				swap(g[i][k],g[j][k]);
		}
		for (ll j=i+1;j<=n;j++){
			ll tmp=1ll*inv(g[i][i])*g[j][i]%mod;
			for (ll k=i;k<=n;k++)
				g[j][k]=(g[j][k]-1ll*tmp*g[i][k]+mod)%mod;
		}
		res=1ll*res*g[i][i]%mod;
	}
	return (res+mod)%mod;
}

void prework(){
	pre[0].len=1,pre[0][0]=0,pre[0][1]=1;
	for (ll i=1;i<=2*n;i++){
		pre[i].clear();
		poly tmp;
		tmp[1]=1,tmp[0]=-i,tmp.len=1;
		pre[i]=pre[i-1]*tmp;
	}
//	pre[1].watch();
//	pre[2].watch();
//	pre[3].watch();
	back[2*n+1].clear();
	for (ll i=2*n;i>=0;i--){
		back[i].clear();
		poly tmp;
		tmp[1]=1,tmp[0]=-i,tmp.len=1;
		back[i]=back[i+1]*tmp;
	}
//	puts("%p");	back[2*n].watch();
//	back[2*n-1].watch();
}

ll solve(ll pw){
	for (ll i=0;i<=2*n;i++){
		memset(g,0,sizeof(g));
		for (ll j=1;j<=m;j++){
			ll u=E[j].u,v=E[j].v,val=qpow(i,E[j].l/pw%3);
			g[u][v]-=val,g[v][u]-=val,g[u][u]+=val,g[v][v]+=val;
		}
		/*if (pw==1)
		for (ll j=1;j<=n;j++,puts(""))
			for (ll k=1;k<=n;k++)
				printf("%d ",g[j][k]);*/
		f[i]=det(n-1);
		//printf("f=%d\n",f[i]);
	}
	//puts(""); 
	for (ll i=0;i<=2*n;i++) h.a[i]=0;
	for (ll i=0;i<=2*n;i++){
		ll down=1;
		for (ll j=0;j<=2*n;j++)
			if (i!=j) down=1ll*down*(i-j)%mod;
		down=1ll*inv(down)*f[i]%mod;
		
		poly up;
		if (i==0) up=back[1]; 
		else up=pre[i-1]*back[i+1];
		//printf("%d::",i),up.watch();
		//if (pw==1) printf("down=%d\n",down);
		for (ll j=2*n;j>=0;j--){
			h[j]=(h[j]+1ll*up[j]*down%mod)%mod;
			//printf("up=%d fx=%d h=%d\n",up[j],down,h[j]);
		}
	}
	ll res=0;//puts("modp");
	for (ll i=0;i<=2*n;i++){
		//printf("h=%d\n",h[i]);
		res=(res+1ll*(i%3)*h[i])%mod;
	}
	return (res+mod)%mod;
}

int main(){
	//freopen(".in","r",stdin);freopen(".out","w",stdout);
	scanf("%d%d",&n,&m);
	for (ll i=1;i<=m;i++) scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].l);
	prework();
	for (ll pw=1,i=0;i<9;i++)
		ans=(ans+1ll*pw*solve(pw))%mod,pw*=3;
	printf("%lld\n",ans);
    return 0;
}

T2:论文题....跳过了

T3:原题不解释

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
const int maxn=2010;
using namespace std;
int n,mod;
ll f[maxn][maxn],g[maxn][maxn],c[maxn][maxn],pw[maxn*maxn];
void read(int &x){
	char ch;
	for (ch=getchar();!isdigit(ch);ch=getchar());
	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}

void getc(){
	c[0][0]=1;
	for (int i=1;i<=n;i++){
		c[i][0]=1;
		for (int j=1;j<=i;j++){
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
			//printf("%d ",c[i][j]);
		}
	}
}

void getg(){
	g[0][0]=1;
	for (int i=1;i<=n;i++){
		int m=i*(i-1)/2%mod;
		g[i][0]=pw[m],g[i][1]=1ll*m*pw[m-1]%mod;
		for (int j=0;j<i;j++)
			g[i][2]=(g[i][2]+(1ll*c[i-1][j]*((g[i-1][2]+2ll*g[i-1][1]*j%mod+1ll*j*j%mod*g[i-1][0]%mod))%mod)%mod)%mod;
		//cout<<g[i][0]<<' '<<g[i][1]<<' '<<g[i][2]<<endl;
	}
}

void getf(){
	memcpy(f,g,sizeof(f));
	for (int i=1;i<=n;i++){
		for (int j=1;j<i;j++){
			f[i][0]=(f[i][0]-1ll*c[i-1][j-1]*f[j][0]%mod*g[i-j][0]%mod+mod)%mod;
			f[i][1]=(f[i][1]-1ll*c[i-1][j-1]*(1ll*f[j][0]*g[i-j][1]%mod+1ll*f[j][1]*g[i-j][0]%mod)%mod+mod)%mod;
			f[i][2]=(f[i][2]-1ll*c[i-1][j-1]*(1ll*f[j][0]*g[i-j][2]%mod+2ll*f[j][1]*g[i-j][1]%mod+1ll*f[j][2]*g[i-j][0]%mod)%mod+mod)%mod;
		}
		//printf("%d %d %d\n",f[i][0],f[i][1],f[i][2]);
	}
}

int main(){
	scanf("%d%d",&n,&mod);
	pw[0]=1;for (int i=1;i<=n*n;i++) pw[i]=(pw[i-1]<<1)%mod;
	getc(),getg(),getf();
	cout<<f[n][2]<<endl;
	return 0;
}

//2000 989184000
//2000 1000000000


你可能感兴趣的:(test5.11)