题目大意
有一棵 n n n个顶点的树,每个点上最多放一张卡片,你可以做如下操作:
- 同时将所有的卡片移到它所在顶点的相邻的一个顶点上
一个操作我们说它是好的,当下列条件满足:
- 每条边最多被某张卡片经过
- 每个顶点最多被一张卡片占据
小
T
T
T可以选择一个或多个顶点来放置卡片,一个顶点放置一张卡片。他有
2
n
−
1
2^n-1
2n−1种方式,求满足以下条件的方案数:
对于每个非负整数
k
k
k
- 它能连续进行 k k k次好的操作
- 令 S k S_k Sk表示经过刚好 k k k次操作后被卡片占据的点的集合,则 S k S_k Sk是唯一的
题解
第一步:分树为链
我们可以发现,每一张卡片都是在两个点上反复横跳的。我们把每个反复横跳的边拿出来,那一定是若干条不相交的链。且这些链一定是以空点为顶部,有卡片的点为中部和尾部(一条链不能只有一个空点)。这些链一定能填满整棵树。
假设 x , y x,y x,y为相邻的两个点且在不同的链上,为了避免重复和不合法的情况,我们做一些规定。
- 如果 x x x链的端点且 y y y为链的中间点,则在第二次操作时,在 y y y上的卡片可以向 x x x移动,则 S k S_k Sk不唯一
- 如果 x , y x,y x,y都是链的顶部,则第一次操作后两条链合并成一条链,可以往两个方向移动, S k S_k Sk不唯一
- 如果 x , y x,y x,y都是链的尾部,则第一次操作时 x x x的位置空出了, y y y所在可以往链头或 x x x移动, S k S_k Sk不唯一
其余情况都是合法的。
第二步:树形DP
定义 f u , i f_{u,i} fu,i表示点 u u u第 i i i种状态下的方案数。各种状态如下:
- f u , 0 f_{u,0} fu,0表示 u u u为链身,且 u u u在链上无前无后
- f u , 1 f_{u,1} fu,1表示 u u u为链身,且 u u u在链上有前无后
- f u , 2 f_{u,2} fu,2表示 u u u为链身,且 u u u在链上无前有后
- f u , 3 f_{u,3} fu,3表示 u u u为链身,且 u u u在链上有前有后
- f u , 4 f_{u,4} fu,4表示 u u u为链头,且 u u u在链上无后面的点
- f u , 5 f_{u,5} fu,5表示 u u u为链头,且 u u u在链上有后面的点
- f u , 6 f_{u,6} fu,6表示 u u u为链尾,且 u u u在链上无后面的点
- f u , 7 f_{u,7} fu,7表示 u u u为链尾,且 u u u在链上有后面的点
有前或有前面的点即存在链头,有后或有后面的点即存在链尾。
为了防止在转移的时候计算重复,我们还需要定义 g u , i g_{u,i} gu,i。假设当前枚举的是 u u u的各个儿子,且枚举到的儿子为 v v v,则 f u , i f_{u,i} fu,i表示点 u u u在统计 v v v之前的各种状态的方案数, g g g表示统计 v v v之后的方案数,则可以用 f u , i f_{u,i} fu,i和 f v , i f_{v,i} fv,i来更新 g g g,在 v v v的贡献计算完之后再将 g g g的值赋值给 f f f,然后计算 u u u的下一个儿子。
因为状态比较多,所以转移式也比较多。除去不合法的情况,有 20 20 20种转移方法,具体见代码。
对于每个点,状态 0 , 4 , 6 0,4,6 0,4,6的 f f f的初值为 1 1 1。最后的答案为 f 1 , 3 + f 1 , 5 + f 1 , 7 f_{1,3}+f_{1,5}+f_{1,7} f1,3+f1,5+f1,7。
总结
这道题主要是用树形DP,考虑各种状态来进行状态转移。时间复杂度为 O ( n ) O(n) O(n)。
注:代码中 g t ( v 1 , v 2 , v 3 ) gt(v1,v2,v3) gt(v1,v2,v3)表示 g u , v 1 = f u , v 2 × f v , v 3 g_{u,v1}=f_{u,v2}\times f_{v,v3} gu,v1=fu,v2×fv,v3, v v v为 u u u的儿子。这一步即用 u u u点的状态 v 2 v2 v2的 f f f和 v v v点的状态 v 2 v2 v2的 f f f值更新 g g g的状态 v 1 v1 v1。
code
#include<bits/stdc++.h>
using namespace std;
int n,x,y,tot=0,d[500005],l[500005],r[500005];
long long v[10],f[200005][8];
long long mod=998244353;
void add(int xx,int yy){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void pt(int v1,int v2,int v3){
v[v1]=(v[v1]+f[x][v2]*f[y][v3]%mod)%mod;
}
void dfs(int u,int fa){
f[u][0]=f[u][4]=f[u][6]=1;
for (int i=r[u];i;i=l[i]){
if(d[i]==fa) continue;
dfs(d[i],u);
for (int j=0;j<8;j++) v[j]=0;
x=u;y=d[i];
pt(0,0,3);
pt(1,0,4);pt(1,0,1);pt(1,1,3);
pt(2,0,6);pt(2,0,2);pt(2,2,3);
pt(3,2,4);pt(3,1,6);pt(3,1,2);pt(3,2,1);pt(3,3,3);
pt(4,4,7);
pt(5,5,7);pt(5,4,2);pt(5,4,6);
pt(6,6,5);
pt(7,7,5);pt(7,6,1);pt(7,6,4);
for(int j=0;j<8;j++) f[u][j]=v[j];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(1,0);
printf("%lld",(f[1][3]+f[1][5]+f[1][7])%mod);
return 0;
}