ARC142D Deterministic Placing

文章描述了一个关于树形数据结构的问题,其中涉及卡片在树的节点上的移动操作。关键在于理解如何将树分解为不相交的链,并通过树形动态规划(DP)解决状态转移问题,以确定满足特定条件的方案数。解决方案包括定义不同状态并处理各种状态间的转移,最终计算特定状态的方案总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ARC142D Deterministic Placing

题目大意

有一棵 n n n个顶点的树,每个点上最多放一张卡片,你可以做如下操作:

  • 同时将所有的卡片移到它所在顶点的相邻的一个顶点上

一个操作我们说它是好的,当下列条件满足:

  • 每条边最多被某张卡片经过
  • 每个顶点最多被一张卡片占据

T T T可以选择一个或多个顶点来放置卡片,一个顶点放置一张卡片。他有 2 n − 1 2^n-1 2n1种方式,求满足以下条件的方案数:
对于每个非负整数 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值