码迷,mamicode.com
首页 > 其他好文 > 详细

【YbtOJ#20081】树上排列

时间:2020-11-30 15:20:42      阅读:2      评论:0      收藏:0      [点我收藏+]

标签:ace   struct   ++   ems   mat   c++   +=   memset   合并   

题目

题目链接:https://www.ybtoj.com.cn/contest/62/problem/3
技术图片

思路

可以看做有多少个 \(1\sim n\) 的排列满足对于一条路径 \(u\to v\)\(u\) 在序列中的位置一定在 \(v\) 在序列中的位置的前面。
\(f[x][i]\) 表示以 \(x\) 为根的子树内,\(x\) 在序列中是第 \(i\) 个数的方案数。
考虑加入 \(x\) 的一个子树 \(y\),枚举在子树 \(y\) 内有多少个数字排在 \(x\) 前面,那么最终合并之后点 \(x\) 是排在位置 \(i+j\) 的。
而前面的 \((i-1)+j\) 个数字可以任意排列,后面同理,所以方案数为

\[f[x][i+j]=\binom{i+j-1}{j}\times \binom{\mathrm{size‘}(x)+\mathrm{size}(y)-i-j}{\mathrm{size}(y)-j}\times f‘[x][i]\times s \]

其中 \(s\)\(y\) 内的方案数,具体的,当 \(p_x<p_y\) 时,\(s=\Pi^{\mathrm{size}(y)}_{k=j+1}f[y][k]\),否则 \(s=\Pi^{j}_{k=1}f[y][k]\)
由于我们只会枚举到 \(\mathrm{size}(y)\),所以两个点只会在他们 LCA 处计算,时间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll N=3010,MOD=998244353;
ll n,tot,f[N][N],g[N],head[N],size[N],C[N][N];
ll ans;

struct edge
{
	ll next,to,id;
}e[N*2];

void add(ll from,ll to,ll id)
{
	e[++tot].to=to;
	e[tot].id=id;
	e[tot].next=head[from];
	head[from]=tot;
}

void dfs(ll x,ll fa)
{
	size[x]=1; f[x][1]=1;
	for (ll i=head[x];~i;i=e[i].next)
	{
		ll v=e[i].to;
		if (v!=fa)
		{
			dfs(v,x);
			size[x]+=size[v];
			for (ll j=0;j<=size[x];j++)
				g[j]=f[x][j],f[x][j]=0;
			ll sum=0;
			if (e[i].id)
				for (ll j=1;j<=size[x]-size[v];j++,sum=0)
					for (ll k=size[v];k>=0;k--)
					{
						f[x][j+k]=(f[x][j+k]+1LL*C[j+k-1][j-1]*C[size[x]-j-k][size[v]-k]%MOD*g[j]%MOD*sum)%MOD;
						sum=(sum+f[v][k])%MOD;
					}
			else
				for (ll j=1;j<=size[x]-size[v];j++,sum=0)
					for (ll k=0;k<=size[v];k++)
					{
						sum=(sum+f[v][k])%MOD;
						f[x][j+k]=(f[x][j+k]+1LL*C[j+k-1][j-1]*C[size[x]-j-k][size[v]-k]%MOD*g[j]%MOD*sum)%MOD;
					}
		}
	}
}

int main()
{
	freopen("perm.in","r",stdin);
	freopen("perm.out","w",stdout);
	memset(head,-1,sizeof(head));
	C[0][0]=C[1][0]=C[1][1]=1;
	for (ll i=2;i<=3000;i++)
	{
		C[i][0]=1;
		for (ll j=1;j<=i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
	}
	scanf("%lld",&n);
	for (ll i=1,x,y;i<n;i++)
	{
		scanf("%lld%lld",&x,&y);
		add(x,y,1); add(y,x,0);
	}
	dfs(1,0);
	for (ll i=1;i<=n;i++)
		ans=(ans+f[1][i])%MOD;
	printf("%lld",ans);
	return 0;
}

【YbtOJ#20081】树上排列

标签:ace   struct   ++   ems   mat   c++   +=   memset   合并   

原文地址:https://www.cnblogs.com/stoorz/p/14032320.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!