给出一棵树,求建出该树的不同操作方案数。建树方式如下:初始
S
S
S集合只有
1
1
1,操作
1
1
1为取已连的边
x
,
y
x,y
x,y和不在
S
S
S的点
z
z
z,删去边
(
x
,
y
)
(x,y)
(x,y),加入边
(
x
,
z
)
,
(
y
,
z
)
(x,z),(y,z)
(x,z),(y,z),再把
z
z
z放入
S
S
S;操作
2
2
2为取
S
S
S内的点
x
x
x和
S
S
S外的点
y
y
y,加入边
(
x
,
y
)
(x,y)
(x,y),再把
y
y
y放入
S
S
S。
2
≤
n
≤
1
0
5
2le n le 10^5
2≤n≤105
题解
可以先分析操作,得出一些结论:
1、
x
∈
S
xin S
x∈S当且仅当
x
x
x与
1
1
1连通;
2、
S
S
S能在操作中出现(即合法)当且仅当
S
S
S中任意两点的
l
c
a
lca
lca也在
S
S
S中;
3、任意一个合法的
S
S
S,只对应唯一一种树的形态,因此操作实质是按一定顺序不断加点;
4、某个子树内的点的加入顺序与子树外无关。
于是可以考虑树形DP,设
f
i
f_i
fi表示
i
i
i子树内的不同顺序方案数,有两种转移。
一、先加入子树根,再儿子内所有点保持各自相对顺序插入(用组合数);
二、先加入某个儿子的部分点(枚举数量),再加入子树根,最后把该儿子剩下的点和其他儿子内所有点保持各自相对顺序插入。(当
i
i
i为
1
1
1时不能使用这种转移)
写出来后发现很多式子都是可以优化的。最后有一个部分式子形如
(
m
0
)
+
(
m
+
1
1
)
+
(
m
+
2
2
)
+
.
.
.
+
(
m
+
k
k
)
{mchoose0}+{m+1choose1}+{m+2choose2}+...+{m+kchoose k}
(0m)+(1m+1)+(2m+2)+...+(km+k),可以把
(
m
0
)
mchoose 0
(0m)看做是
(
m
+
1
0
)
m+1choose 0
(0m+1),然后在杨辉三角上就能不断合并下来,最后结果是
(
m
+
k
+
1
k
)
m+k+1choose k
(km+k+1)。
由于快速幂,最后复杂度是
O
(
n
log
n
)
O(nlog n)
O(nlogn)。
代码
#include<cstdio>#include<cstring>#include<algorithm>usingnamespace std;#define N 100010#define md 998244353#define ll long longint last[N], nxt[N *2], to[N *2], len =0;int si[N], f[N];
ll F[N], G[N];int ans =0;
ll ksm(ll x, ll y){if(!y)return1;
ll l =ksm(x, y /2);if(y %2)return l * l % md * x % md;return l * l % md;}
ll C(int x,int y){return F[x]* G[y]% md * G[x - y]% md;}voidadd(int x,int y){
to[++len]= y;
nxt[len]= last[x];
last[x]= len;}voiddfs(int k,int fa){
si[k]=1;
ll sum =1;for(int i = last[k]; i; i = nxt[i])if(to[i]!= fa){dfs(to[i], k);
si[k]+= si[to[i]];
sum = sum *C(si[k]-1, si[to[i]])% md * f[to[i]]% md;}
f[k]= sum;if(k ==1)return;for(int i = last[k]; i; i = nxt[i])if(to[i]!= fa){int x = to[i];
ll s =C(si[k]-1, si[x]-1);
ll t = f[x]* sum % md *ksm(f[x]*C(si[k]-1, si[x])% md, md -2)% md;
f[k]=(f[k]+ s * t % md)% md;}}intmain(){int n, i, x, y;scanf("%d",&n);
F[0]= G[0]=1;for(i =1; i <= n; i++) F[i]= F[i -1]* i % md;
G[n]=ksm(F[n], md -2);for(i = n -1; i; i--) G[i]= G[i +1]*(i +1)% md;for(i =1; i < n; i++){scanf("%d%d",&x,&y);add(x, y),add(y, x);}dfs(1,0);printf("%lld
", f[1]);return0;}