Description

\(\mathcal{P}\text{ortal.}\)

Solution

Method 1:矩阵树定理

麻了,之前还做过一道把边权写成多项式的矩阵树定理题,结果这题还是不会做 qwq.

设给定边的边权为 \(x\),其余边的边权为 \(1\),那么求出所有生成树的边权之积的和就是一个 \(n-1\) 次的多项式(考虑求行列式乘了 \(n-1\) 次),那么 \(x^k\) 项对应的系数就是有 \(k\) 条给定边的生成树个数。

如果直接用多项式进行高斯消元求行列式,复杂度则有 \(\mathcal O(n^4\log n)\)(最里面的循环有多项式乘法,所以带 \(\mathcal O(n\log n)\) 的复杂度),而且目测不会很好写。事实上可以拉格朗日插值,带入 \(x=1,2,\dots, n\) 计算 \(n\) 次行列式求得 \(n\) 个函数值,再将多项式依次降次,取 \(f(0)\) 作为系数,复杂度降到了 \(\mathcal O(n^4)\).

Method 2:容斥

\(\mathcal{C}\text{onclusion}\):一个 \(n\) 个点的无向图,若连通块个数为 \(k\),大小分别为 \(s_1,s_2,\dots,s_k\),则用 \(k-1\) 条边将 \(k\) 个连通块连起来的方案数为 \(n^{k-2}\cdot \prod_{i=1}^k s_i\).

证明就直接 \(\text{oi-wiki}\) 了。

\(g(i)\)至少\(i\) 条给定边的方案数,一个非常神奇的等价是这等价于在 给定树 上取 \(n-i\) 个连通块(也就是取 \(i\) 条给定边),其余边乱选的方案数!如果算出 \(g(i)\),那么代表恰好的 \(f(i)\) 用二项式反演也就可以算出来了。

\(g(i,j,k)\) 为以 \(i\) 为根的子树划分了 \(j\) 个连通块,当前点所在块大小为 \(k\)。由上文结论,我们只用维护 \(\prod_{i=1}^k s_i\)。那么 \(\mathcal O(n^3)\) 可以简单转移。

事实上还可以做到更优,类似 这题 的转化,我们考虑 \(\prod_{i=1}^k s_i\) 的组合意义:在每个连通块中选一个点的方案数。设 \(g(i,j,0/1)\) 表示以 \(i\) 为根的子树划分了 \(j\) 个连通块,当前点所在块是否已选点的方案数,复杂度优化到 \(\mathcal O(n^2)\).

Code

Method 1:矩阵树定理

犯了很多神必错误,感觉自己像个脑瘫。

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while(!isdigit(s=getchar())) f|=(s=='-');
    for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    return f? -x: x;
}
template <class T>
inline void write(T x) {
    static int writ[50], w_tp=0;
    if(x<0) putchar('-'), x=-x;
    do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    while(putchar(writ[w_tp--]^48), w_tp);
}

# include <iostream>
using namespace std;
typedef pair <int,int> par;

const int maxn = 105;
const int mod = 1e9+7;

inline int inv(int x,int y=mod-2,int r=1) {
	for(; y; y>>=1, x=1ll*x*x%mod)
		if(y&1) r=1ll*r*x%mod;
	return r;
}
inline void dec(int& x,int y) { x=(x-y<0?x-y+mod:x-y); }
inline void inc(int& x,int y) { x=(x+y>=mod?x+y-mod:x+y); }
inline int _dec(int x,int y) { return x-y<0?x-y+mod:x-y; }

par val[maxn];
bool arr[maxn][maxn];
int n,a[maxn][maxn];

inline int gauss(int n) {
	int ret=1, j, Inv, tmp; bool f=0;
	for(int i=1;i<=n;++i) {
		for(j=i; j<=n && !a[j][i]; ++j);
		if(i^j) swap(a[i],a[j]), f^=1;
		Inv = inv(a[i][i]);
		ret = 1ll*ret*a[i][i]%mod;
		for(j=i+1;j<=n;++j) if(a[j][i]) {
			tmp = 1ll*Inv*a[j][i]%mod;
			for(int k=i;k<=n;++k)
				dec(a[j][k],1ll*tmp*a[i][k]%mod);
		}
	}
	return f? mod-ret: ret;
}

inline void initialize(int x) {
	for(int i=1;i<=n;++i) {
		int tmp=0;
		for(int j=1;j<=n;++j) if(i^j) {
			if(arr[i][j]) a[i][j] = mod-x;
			else a[i][j] = mod-1;
			tmp += mod-a[i][j];
		}
		a[i][i] = tmp;
	}
}

int main() {
	n=read(9);
	for(int i=1;i<n;++i) {
		int u=read(9), v=read(9);
		arr[u][v]=arr[v][u]=1;
	}
	for(int x=1;x<=n;++x) {
		initialize(x); // 不知道自己为啥要写 pair……也就图一乐😅
		val[x] = make_pair(x,gauss(n-1));
	}
	for(int k=0;k<n;++k) {
		int tmp; long long ans=0;
		for(int i=k+1;i<=n;++i) {
			tmp=1;
			for(int j=k+1;j<=n;++j) if(i^j)
				tmp = -1ll*tmp*j%mod*inv(_dec(i,j))%mod;
			ans += 1ll*val[i].second*tmp%mod;
		} 
		print(ans=(ans%mod+mod)%mod,' ');
		for(int i=k+2;i<=n;++i)
			dec(val[i].second,ans),
			val[i].second = 1ll*val[i].second*inv(i)%mod;
	} puts("");
	return 0;
}

Method 2:容斥

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while(!isdigit(s=getchar())) f|=(s=='-');
    for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    return f? -x: x;
}
template <class T>
inline void write(T x) {
    static int writ[50], w_tp=0;
    if(x<0) putchar('-'), x=-x;
    do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    while(putchar(writ[w_tp--]^48), w_tp);
}

# include <vector>
using namespace std;

const int maxn = 105;
const int mod = 1e9+7;

inline int inv(int x,int y=mod-2,int r=1) {
	for(; y; y>>=1, x=1ll*x*x%mod)
		if(y&1) r=1ll*r*x%mod;
	return r;
}
inline void inc(int& x,int y) { x=(x+y>=mod?x+y-mod:x+y); }
inline void dec(int& x,int y) { x=(x-y<0?x-y+mod:x-y); }
inline int _inc(int x,int y) { return x+y>=mod?x+y-mod:x+y; }

vector <int> e[maxn];
int fac[maxn],ifac[maxn],G[maxn];
int n,g[maxn][maxn][2],tmp[maxn][2],sz[maxn];

void dfs(int u,int fa) {
	g[u][1][1]=g[u][1][0]=1; sz[u]=1;
	for(const auto& v:e[u]) if(v^fa) {
		dfs(v,u); 
		for(int i=1;i<=sz[u]+sz[v];++i) 
			tmp[i][0]=tmp[i][1]=0;
		for(int i=1;i<=sz[u];++i)
			for(int j=1;j<=sz[v];++j) {
				inc(tmp[i+j][0],1ll*g[u][i][0]*g[v][j][1]%mod);
				inc(tmp[i+j][1],1ll*g[u][i][1]*g[v][j][1]%mod);
				inc(tmp[i+j-1][0],1ll*g[u][i][0]*g[v][j][0]%mod);
				inc(tmp[i+j-1][1],(1ll*g[u][i][0]*g[v][j][1]+1ll*g[u][i][1]*g[v][j][0])%mod);
			}
		sz[u] += sz[v];
		for(int i=1;i<=sz[u];++i)
			for(int j=0;j<2;++j) g[u][i][j]=tmp[i][j];
	}
}

inline void initialize() {
	for(int i=fac[0]=1;i<=n;++i)
		fac[i] = 1ll*fac[i-1]*i%mod;
	ifac[n] = inv(fac[n]);
	for(int i=n-1;i>=0;--i)
		ifac[i] = 1ll*ifac[i+1]*(i+1)%mod;
}
inline int C(int n,int m) {
	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}

int main() {
	n=read(9); initialize();
	for(int i=1;i<n;++i) {
		int u=read(9), v=read(9);
		e[u].emplace_back(v),
		e[v].emplace_back(u);
	}
	dfs(1,0); G[1]=1;
	for(int i=2;i<=n;++i) 
		G[i] = 1ll*inv(n,i-2)*g[1][i][1]%mod;
	for(int i=0;i<n;++i) {
		int ans=0;
		for(int j=i;j<n;++j) {
			int tmp = 1ll*C(j,i)*G[n-j]%mod;
			if(j-i&1) dec(ans,tmp);
			else inc(ans,tmp);
		} print(ans,' ');
	} puts("");
	return 0;
}
posted on 2022-05-25 23:20  Oxide  阅读(32)  评论(0编辑  收藏  举报