题解 计树 / [ICPC2021-2022 澳门] Permutation on Tree

传送门
Permutation on Tree

你考虑这么一个事,就是

  1. 题解做法巨恶心
  2. 我写的题解做法
  3. 只有我写的题解做法

然后是具体做法:
发现贡献是绝对值的形式,类似这个的拆开考虑
那么 \(\forall i\in[1, n-1]\),考虑将序列中 \(\leqslant x\) 的元素记为 1,\(>x\) 的元素记为 0
然后 DP 出所有 01 序列的方案数,再 DP 出每个位置和下一个位置 01 相邻的方案数
对后面那个东西求和就是答案
那么令 \(f_{i, j, 0/1}\) 为仅考虑 \(i\) 子树中的点构成的排列,第 \(j\) 个位置为 \(0/1\) 的方案数
再令 \(g_{i, j}\) 为仅考虑 \(i\) 子树中的点构成的排列,第 \(j\) 个位置和第 \(j+1\) 个位置不同的方案数
转移极其恶心
大致是考虑将 \(v\) 子树并入 \(u\) 子树
那么对于 \(v\) 子树中排名为 \(j\in[1, siz_v]\) 的一个点,枚举其在 \(u\) 子树中的排名 \(i\),合并到位置 \(i+j\) 即可
因为有限制条件“父节点权值小于子节点权值”
所以合并的时候要对子树的根节点各种处理
我也就写了一年
复杂度 \(O(n^3)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 210
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, rot;
vector<int> to[N];
const ll mod=1e9+7;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}

namespace force{
	int son[N];
	ll f[1<<20][20], g[1<<20][20], ans;
	void dfs(int u, int fa) {for (auto v:to[u]) if (v!=fa) dfs(v, u), son[u]|=1<<v;}
	void solve() {
		dfs(rot, -1);
		int lim=1<<n;
		for (int i=0; i<n; ++i) g[1<<i][i]=!son[i];
		for (int s=1; s<lim; ++s)
			for (int i=0; i<n; ++i) if (g[s][i])
				for (int j=0; j<n; ++j) if (!(s&(1<<j)) && (son[j]&s)==son[j])
					md(g[s|(1<<j)][j], g[s][i]);
		for (int s=1; s<lim; ++s)
			for (int i=0; i<n; ++i) if (f[s][i]||g[s][i])
				for (int j=0; j<n; ++j) if (!(s&(1<<j)) && (son[j]&s)==son[j])
					f[s|(1<<j)][j]=(f[s|(1<<j)][j]+f[s][i]+g[s][i]*abs(i-j))%mod;
		for (int i=0; i<n; ++i) ans=(ans+f[lim-1][i])%mod;
		printf("%lld\n", ans);
	}
}

namespace task1{
	ll fac[N], ans;
	void solve() {
		fac[0]=fac[1]=1;
		for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
		for (int i=0; i<n; ++i) if (i!=rot)
			ans=(ans+abs(rot-i)*fac[n-2])%mod;
		for (int i=2; i<n; ++i)
			for (int j=0; j<n; ++j) if (j!=rot)
				for (int k=0; k<n; ++k) if (k!=rot&&k!=j)
					ans=(ans+abs(j-k)*fac[n-3])%mod;
		printf("%lld\n", ans);
	}
}

namespace task{
	int siz[N], x;
	ll fac[N<<1], inv[N<<1];
	ll f[N][N][2], g[N][N], ans;
	inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
	void dfs(int u, int fa) {
		// cout<<"dfs: "<<u<<' '<<fa<<endl;
		f[u][1][u<=x]=1; siz[u]=1;
		ll t1[N][2], t2[N];
		for (auto v:to[u]) if (v!=fa) {
			dfs(v, u);
			// cout<<"merge: "<<u<<' '<<v<<endl;
			memset(t1, 0, sizeof(t1));
			for (int i=1; i<=siz[u]; ++i) {
				for (int j=1; j<=siz[v]; ++j) {
					for (int k=0; k<2; ++k)
						t1[i+j][k]=(t1[i+j][k]+(f[u][i][0]+f[u][i][1])*f[v][j][k]%mod*C(i-1+j-1, j-1)%mod*C(siz[u]-i+siz[v]-j, siz[v]-j))%mod;
					if (i!=1) {
						for (int k=0; k<2; ++k)
							t1[i+j][k]=(t1[i+j][k]+f[u][i][k]*(f[v][j][0]+f[v][j][1])%mod*C(i-2+j, i-2)%mod*C(siz[u]-i+siz[v]-j, siz[u]-i))%mod;
					}
				}
				for (int k=0; k<2; ++k) t1[i][k]=(t1[i][k]+f[u][i][k]*(f[v][1][0]+f[v][1][1])%mod*C(siz[u]-i+siz[v], siz[v]))%mod;
			}
			// cout<<"t1(0): "; for (int i=1; i<=n; ++i) cout<<t1[i][0]<<' '; cout<<endl;
			// cout<<"t1(1): "; for (int i=1; i<=n; ++i) cout<<t1[i][1]<<' '; cout<<endl;
			memset(t2, 0, sizeof(t2));
			for (int i=2; i<=siz[u]; ++i) {
				for (int j=1; j<=siz[v]; ++j) {
					for (int k=0; k<2; ++k)
						t2[i+j-1]=(t2[i+j-1]+f[u][i][k^1]*f[v][j][k]%mod*C(i-2+j-1, j-1)%mod*C(siz[u]-i+siz[v]-j, siz[v]-j))%mod;
					for (int k=0; k<2; ++k)
						t2[i+j-1]=(t2[i+j-1]+f[u][i][k^1]*f[v][j][k]%mod*C(i-2+j-1, i-2)%mod*C(siz[u]-i+siz[v]-j, siz[u]-i))%mod;
				}
			}
			for (int k=0; k<2; ++k) t2[1]=(t2[1]+f[u][1][k^1]*f[v][1][k]%mod*C(siz[u]-1+siz[v]-1, siz[u]-1))%mod;
			for (int i=2; i<siz[u]; ++i)
				for (int j=0; j<=siz[v]; ++j)
					t2[i+j]=(t2[i+j]+g[u][i]*(f[v][1][0]+f[v][1][1])%mod*C(i-2+j, j)%mod*C(siz[u]-i-1+siz[v]-j, siz[u]-i-1))%mod;
			if (siz[u]>1) t2[1]=(t2[1]+g[u][1]*(f[v][1][0]+f[v][1][1])%mod*C(siz[u]-2+siz[v], siz[v]))%mod;
			for (int j=1; j<siz[v]; ++j)
				for (int i=1; i<=siz[u]; ++i)
					t2[i+j]=(t2[i+j]+g[v][j]*(f[u][1][0]+f[u][1][1])%mod*C(j-1+i-1, j-1)%mod*C(siz[v]-j-1+siz[u]-i, siz[v]-j-1))%mod;
			// cout<<"t2: "; for (int i=1; i<=n; ++i) cout<<t2[i]<<' '; cout<<endl;
			memcpy(f[u], t1, sizeof(t1));
			memcpy(g[u], t2, sizeof(t2));
			siz[u]+=siz[v];
		}
		// cout<<"return "<<u<<endl;
		// cout<<"f(0): "; for (int i=1; i<=n; ++i) cout<<f[u][i][0]<<' '; cout<<endl;
		// cout<<"f(1): "; for (int i=1; i<=n; ++i) cout<<f[u][i][1]<<' '; cout<<endl;
	}
	ll solve(int x) {
		// cout<<"solve: "<<x<<endl;
		task::x=x;
		memset(f, 0, sizeof(f));
		memset(g, 0, sizeof(g));
		dfs(rot, 0);
		ll ans=0;
		for (int i=1; i<n; ++i) ans=(ans+g[rot][i])%mod;
		// cout<<"return: "<<ans<<endl;
		return ans;
	}
	void solve() {
		fac[0]=fac[1]=1; inv[0]=inv[1]=1;
		for (int i=2; i<=(n<<1); ++i) fac[i]=fac[i-1]*i%mod;
		for (int i=2; i<=(n<<1); ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (int i=2; i<=(n<<1); ++i) inv[i]=inv[i-1]*inv[i]%mod;
		// solve(4);
		for (int i=1; i<n; ++i) ans=(ans+solve(i))%mod;
		printf("%lld\n", ans);
	}
}

signed main()
{
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);

	n=read(); rot=read();
	for (int i=1,u,v; i<n; ++i) {
		u=read(); v=read();
		to[u].pb(v); to[v].pb(u);
	}
	// if (n<=20) force::solve();
	// else task1::solve();
	task::solve();

	return 0;
}
posted @ 2022-07-06 21:35  Administrator-09  阅读(4)  评论(0编辑  收藏  举报