• 【题解】P4516 [JSOI2018] 潜入行动


    【题解】P4516 [JSOI2018] 潜入行动

    比较常规但是有点思维含量的一道树形 DP。


    题目链接

    P4516 [JSOI2018] 潜入行动

    题意概述

    给定一棵有 nn 个点的树,现在要在一些点上安装最多 kk 个监听设备,每个监听设备可以监听到与这个点直接连边的所有点,但不可以监听到它本身,现在要求每个点都被监听到,且每个点最多只能有一个监听设备,求有多少种合法的方案,答案对 1e9+7 取模

    题目分析

    首先比较显然的是个树上背包

    那么按照树上背包的套路,我们定义 dpx,i 表示的是以 x 为根的子树,安装了 i 个监听设备有多少种合法的方案。

    但是发现直接这样定义并不好转移,必须要考虑 x 本身是否放了监听设备以及有没有被监听。

    所以我们更改一下 dp 状态:定义 dpx,i,0/1,0/1 表示的是以 x 为根的子树,安装了 i 个监听设备,x 是否安装了监听设备,x 是否被监听的方案数。

    那么最后的答案就是 dp1,k,0,1+dp1,k,1,1

    树上背包的转移套路是考虑合并子树,即考虑 dpx,i+j 怎么样由 dpx,idpy,j 转移而来,其中 yx 的儿子。

    对于这样比较复杂的状态,我们分别考虑以 x 为根的每个状态应该怎么转移。

    • 对于 dpx,i+j,0,0

      x 没有被监听,说明 y 一定没有安装监听设备;x 没有安装设备,说明 y 一定需要被除 x 之外的其它点监听。

      转移方程:

      dpx,i+j,0,0=dpx,i,0,0×dpy,j,0,1
    • 对于 dpx,i+j,0,1

      x 没有安装设备,说明 y 一定被其它点监听;x 被监听了,有两种情况:

      1. x 在考虑 y 之前就已经被其它儿子监听,此时 y 之前安装不安装设备无所谓;
      2. x 在考虑 y 之前还未被其它儿子监听,所以它只能被 y 监听,即 y 必须安装设备。

      转移方程:

      dpx,i+j,0,1=dpx,i,0,1×(dpy,i,0,1+dpy,j,1,1)+dpx,i,0,0×dpy,j,1,1
    • 对于 dpx,i+j,1,0

      x 安装设备了,那么 y 之前是否被监听无所谓,因为反正它至少会被 x 监听;x 没有被监听,那么 y 一定不能安装设备。

      转移方程:

      dpx,i+j,1,0=dpx,i,1,0×(dpy,j,0,1+dpy,j,0,0)
    • 对于 dpx,i+j,1,1

      x 安装设备了,那么 y 之前是否被监听无所谓,因为反正它至少会被 x 监听;x 被监听了,有两种情况:

      1. x 在考虑 y 之前就已经被其它儿子监听,此时 y 之前安装不安装设备无所谓;
      2. x 在考虑 y 之前还未被其它儿子监听,所以她只能被 y 监听,即 y 必须安装设备。

      考虑 xy 之前是否被监听时,同时考虑 y 之前是否被监听,总转移方程如下:

      dpx,i+j,1,1=dpx,i,1,1×(dpy,i,1,1+dpy,i,1,0+dpy,i,0,1+dpy,i,1,1)//x y +dpx,i,1,0×(dpy,i,1,1+dpy,i,1,0)//x y 

    这就是全部的转移。

    易错点

    • 开 long long 会 MLE,请使用 unsigned int;
    • 该取模的地方要取模;
    • 树上背包每次转移前,先将 dpx,i,0/1,0/1 下的每一个状态都存一个临时数组下面只用临时数组转移,并将所有原 $dp_{x,i,0/1,0/1} $ 归零,防止之后的转移出现错乱。

    代码实现

    1. //luoguP4516
    2. #include
    3. #include
    4. #include
    5. #define int unsigned int
    6. using namespace std;
    7. const int maxn=1e5+10;
    8. const int maxk=105;
    9. const int mod=1e9+7;
    10. int dp[maxn][maxk][2][2],tp[maxk][2][2];
    11. int sz[maxn];
    12. int n,k;
    13. basic_string<int>edge[maxn];
    14. inline int read()
    15. {
    16. int x=0,f=1;char ch=getchar();
    17. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    18. while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    19. return x*f;
    20. }
    21. void dfs(int x,int fa)
    22. {
    23. sz[x]=dp[x][0][0][0]=dp[x][1][1][0]=1;
    24. for(int y:edge[x])
    25. {
    26. if(y==fa)continue;
    27. dfs(y,x);
    28. for(int i=0;i<=min(sz[x],k);i++)
    29. {
    30. tp[i][0][0]=dp[x][i][0][0];
    31. tp[i][0][1]=dp[x][i][0][1];
    32. tp[i][1][0]=dp[x][i][1][0];
    33. tp[i][1][1]=dp[x][i][1][1];
    34. dp[x][i][0][0]=0;
    35. dp[x][i][0][1]=0;
    36. dp[x][i][1][0]=0;
    37. dp[x][i][1][1]=0;
    38. }
    39. for(int i=0;i<=min(sz[x],k);i++)
    40. {
    41. for(int j=0;j<=min(sz[y],k-i);j++)
    42. {
    43. (dp[x][i+j][0][0]+=1ll*tp[i][0][0]*dp[y][j][0][1]%mod)%=mod;
    44. (dp[x][i+j][0][1]+=1ll*tp[i][0][0]*dp[y][j][1][1]%mod+1ll*tp[i][0][1]*(dp[y][j][0][1]+dp[y][j][1][1])%mod)%=mod;
    45. (dp[x][i+j][1][0]+=1ll*tp[i][1][0]*(dp[y][j][0][0]+dp[y][j][0][1])%mod)%=mod;
    46. (dp[x][i+j][1][1]+=1ll*tp[i][1][0]*(dp[y][j][1][0]+dp[y][j][1][1])%mod+1ll*tp[i][1][1]*(dp[y][j][0][0]+dp[y][j][0][1]+dp[y][j][1][0]+dp[y][j][1][1])%mod)%=mod;
    47. }
    48. }
    49. sz[x]+=sz[y];
    50. }
    51. }
    52. signed main()
    53. {
    54. n=read();k=read();
    55. for(int i=1;i
    56. {
    57. int u,v;
    58. u=read();v=read();
    59. edge[u]+=v;
    60. edge[v]+=u;
    61. }
    62. dfs(1,0);
    63. cout<<1ll*(dp[1][k][0][1]+dp[1][k][1][1])%mod<<"\n";
    64. return 0;
    65. }
  • 相关阅读:
    9.2 安卓逆向之—Frida持久化方案
    【数据结构】时间复杂度---OJ练习题
    同样做软件测试,和月收入 3W 的学弟聊了一晚上,我彻底崩溃了
    MySQL知识点总结(五)——锁
    XSS-labs靶场实战(五)——第12-14关
    c++ 小案例:判断质数&&猜数字&&用符号填补心形图案
    全网唯一 | 互联网公司微信支付宝服务商入驻流程图文指南
    第二十二章《记事本》第2节:记事本功能实现
    如何利用低代码做好系统整合,实现企业统一管理?
    详细堆排序的实现
  • 原文地址:https://blog.csdn.net/m0_46222454/article/details/127736996