题面传送门

一眼树形 \(dp\)

本题有 \(2\) 大难点。

难点之一是状态的设计,这里需要四维状态,\(dp[i][j][0/1][0/1]\) 表示在以 \(i\) 为根的子树内放了 \(j\) 个监听器,\(i\) 号点是否放了监听器,\(i\) 号点是否被它的儿子覆盖,在这种情况下的方案数。

设计好了状态,转移也就水到渠成了。

\(dp[u][j][0][0]\) 只能从 \(dp[v][j][0][1]\) 转移:\(i\) 号节点没放监听设备也没被覆盖,说明它的儿子都没放监听设备,并且它的儿子只能被它的儿子的儿子所覆盖。

\(dp[u][j][0][1]\) 可以从 \(dp[v][j][0][1]\) 和 \(dp[v][j][1][1]\) 转移过来。但还需减掉 \(dp[u][j][0][0]\) 的情况:\(i\) 号节点没放监听设备但被覆盖,说明它所有儿子都没放监听器,至于它的儿子有没有被覆盖,怎么样都行。

\(dp[u][j][1][0]\) 可以从 \(dp[v][j][0][0]\) 和 \(dp[v][j][0][1]\) 转移过来:\(i\) 号节点放了监听设备但没被覆盖,说明它至少一个儿子放了监听器,并且它的儿子只能被它的儿子的儿子所覆盖。

\(dp[u][j][1][1]\) 可以从 \(dp[v][j][0/1][0/1]\) 转移过来。但还需减掉 \(dp[u][j][1][0]\) 的情况。

至于第二维,合并两个子树的时候跑个树上背包就可以了。

难点之二是复杂度的计算。

说实话这题一开始我想到正解了可不知道它能过。

暴力合并其实是 \(\mathcal O(nk)\) 而不是 \(\mathcal O(nk^2)\) 的,下面给出简单的证明(开始抄题解ing):

  1. 若合并两个大小 \(>k\) 的子树,由于这样的子树最多 \(\frac{n}{k}\) 个,暴力合并复杂度是 \(nk\) 的。
  2. 若合并一棵大小 \(\leq k\) 的和一棵大小 \(>k\) 的子树,这样那个大小 \(\leq k\) 的子树就变成了大小 \(>k\) 的子树。由于每个点最多只在它的某个祖先处被合并一次,这样复杂度均摊也是 \(nk\) 的。
  3. 若合并两棵大小 \(\leq k\) 的子树,那相当于对两棵子树中每个点都合并了一次。而合并之后得到的子树的大小 \(\leq 2k\),故每个点最多与 \(2k\) 个这样的点进行了合并,故复杂度还是 \(nk\) 的。

    证明比较玄乎,大概看看即可。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
const ll MOD=1e9+7;
int n,k,siz[100005];
vector<int> g[100005];
int dp[100005][105][2][2];
int tmp[105][2][2];
inline void dfs(int x,int f){
siz[x]=1;
for(int i=0;i<g[x].size();i++){
int y=g[x][i];if(y==f) continue;
dfs(y,x);
}
dp[x][0][0][0]=dp[x][1][1][0]=dp[x][0][0][1]=dp[x][1][1][1]=1;
for(int i=0;i<g[x].size();i++){
int y=g[x][i];if(y==f) continue;
memset(tmp,0,sizeof(tmp));
for(int j=0;j<=min(siz[y],k);j++) for(int l=0;l<=min(siz[x],k-j);l++){
tmp[j+l][0][0]=(tmp[j+l][0][0]+1ll*dp[x][l][0][0]*dp[y][j][0][1]%MOD)%MOD;
tmp[j+l][0][1]=(tmp[j+l][0][1]+1ll*dp[x][l][0][1]*(dp[y][j][0][1]+dp[y][j][1][1])%MOD)%MOD;
tmp[j+l][1][0]=(tmp[j+l][1][0]+1ll*dp[x][l][1][0]*(dp[y][j][0][0]+dp[y][j][0][1])%MOD)%MOD;
tmp[j+l][1][1]=(tmp[j+l][1][1]+1ll*dp[x][l][1][1]*(((dp[y][j][0][0]+dp[y][j][0][1])%MOD+dp[y][j][1][0])%MOD+dp[y][j][1][1])%MOD)%MOD;
}
for(int j=0;j<=min(siz[x]+siz[y],k);j++){
dp[x][j][0][0]=tmp[j][0][0];dp[x][j][0][1]=tmp[j][0][1];
dp[x][j][1][0]=tmp[j][1][0];dp[x][j][1][1]=tmp[j][1][1];
}
siz[x]+=siz[y];
}
for(int j=0;j<=k;j++){
dp[x][j][0][1]=(dp[x][j][0][1]-dp[x][j][0][0]+MOD)%MOD;
dp[x][j][1][1]=(dp[x][j][1][1]-dp[x][j][1][0]+MOD)%MOD;
}
// for(int j=0;j<=k;j++) for(int p=0;p<2;p++) for(int q=0;q<2;q++){
// printf("%d %d %d %d %d\n",x,j,p,q,dp[x][j][p][q]);
// }
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
g[u].pb(v);g[v].pb(u);
}
dfs(1,0);int ans=0;
for(int x=0;x<2;x++) ans=(ans+dp[1][k][x][1])%MOD;
printf("%d\n",ans);
return 0;
}
/*
5 3
1 2
1 3
2 4
2 5 6 3
1 2
1 3
2 4
2 5
3 6
*/

洛谷 P4516 [JSOI2018]潜入行动的更多相关文章

  1. 洛谷P4559 [JSOI2018]列队 【70分二分 + 主席树】

    题目链接 洛谷P4559 题解 只会做\(70\)分的\(O(nlog^2n)\) 如果本来就在区间内的人是不用动的,区间右边的人往区间最右的那些空位跑,区间左边的人往区间最左的那些空位跑 找到这些空 ...

  2. luogu P4516 [JSOI2018]潜入行动

    LINK:潜入行动 初看题感觉很不可做 但是树形dp的状态过于明显. 容易设\(f_{x,j,l,r}\)表示x为根子树内放了j个设备且子树内都被覆盖l表示x是否被覆盖r表示x是否放设备的方案数. 初 ...

  3. 洛谷P4518 [JSOI2018]绝地反击(计算几何+二分图+退流)

    题面 传送门 题解 调了咱一个上午-- 首先考虑二分答案,那么每个点能够到达的范围是一个圆,这个圆与目标圆的交就是可行的区间,这个区间可以用极角来表示 首先,如果我们知道这个正\(n\)边形的转角,也 ...

  4. 洛谷P4517 [JSOI2018]防御网络(dp)

    题面 传送门 题解 翻译一下题意就是每次选出一些点,要用最少的边把这些点连起来,求期望边数 我也不知道为什么反正总之就是暴力枚举太麻烦了所以我们考虑贡献 如果一条边是割边,那么它会在图里当且仅当两边的 ...

  5. 洛谷P4557 [JSOI2018]战争(闵可夫斯基和+凸包)

    题面 传送门 题解 看出这是个闵可夫斯基和了然而我当初因为见到这词汇是在\(shadowice\)巨巨的\(Ynoi\)题解里所以压根没敢学-- 首先您需要知道这个 首先如果有一个向量\(w\)使得\ ...

  6. 洛谷P4559 [JSOI2018]列队(主席树)

    题面 传送门 题解 首先考虑一个贪心,我们把所有的人按\(a_i\)排个序,那么排序后的第一个人到\(k\),第二个人到\(k+1\),...,第\(i\)个人到\(k+i-1\),易证这样一定是最优 ...

  7. 洛谷 P4559: bzoj 5319: [JSOI2018]军训列队

    题目传送门:洛谷 P4559. 题意简述: 有 \(n\) 个学生,编号为 \(i\) 的学生有一个位置 \(a_i\). 有 \(m\) 个询问,每次询问编号在 \([l,r]\) 区间内的学生跑到 ...

  8. 【BZOJ5314】[JSOI2018]潜入行动(动态规划)

    [BZOJ5314][JSOI2018]潜入行动(动态规划) 题面 BZOJ 洛谷 题解 不难想到一个沙雕\(dp\),设\(f[i][j][0/1][0/1]\)表示当前点\(i\),子树中一共放了 ...

  9. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

随机推荐

  1. python的函数参数传递方式

    python的一切数据类型都是对象.但是python的对象分为不可变对象和可变对象.python的变量是引用,对python变量的赋值是引用去绑定该对象. 可变对象的数据发生改变,例如列表和字典,引用 ...

  2. springboot 事务执行全流程分析

    springboot 事务执行全流程分析 目录 springboot 事务执行全流程分析 1. 事务方法执行前的准备工作 2. 业务代码的调用 3. 事务方法执行后处理 4. 业务代码在事务和非事务中 ...

  3. SpringCloud-SpringBoot-SpringCloudAlibaba对应版本选择

    一.SpringCloud-SpringBoot 对应的版本选择 SpringCloud官网常规方式只能查看最新的几个版本信息 https://spring.io/projects/spring-cl ...

  4. Java正则中"\\\\"表示普通反斜杠

    Java中"\"用于转义字符,"\\"表示普通无转义功能的反斜杠. 如果将字符串当做正则表达式来解析,那么"\\"也有了特殊意义,它与其后的 ...

  5. Scrum Meeting 1补充会议

    日期:2021年04月24日 会议主要内容概述: 本次会议于11:30举行,对项目架构做出了重要调整,并根据该调整修改了第1次例会报告中后两日计划完成的工作部分. 一.架构调整 会上讨论了用户模块相关 ...

  6. Scrum Meeting 0607

    零.说明 日期:2021-6-7 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 困难 qsy PM&前端 重新设计产品 ...

  7. Alpha阶段发布声明

    发布声明 Alpha 1.Alpha版本功能说明 功能列表和详情图 模块 功能 展示 首页 查看首页博文,搜索博文,可供未登录用户使用 动态 查看推荐动态给未登录用户使用,登录用户可以查看关注动态.我 ...

  8. shell IO重定向

    I/O重定向 默认情况下,有3个"文件"处于打开状态,stdin,stdout,stderr:重定向的解释:捕捉一个文件,命令,程序,脚本或者脚本中的代码块的输出,然后将这些输出作 ...

  9. linux 内核源代码情景分析——用户堆栈的扩展

    上一节中,我们浏览了一次因越界访问而造成映射失败从而引起进程流产的过程,不过有时候,越界访问时正常的.现在我们就来看看当用户堆栈过小,但是因越界访问而"因祸得福"得以伸展的情景. ...

  10. 修改linux 两种时间的方法

    1,整理了一下怎么修改linux 两种时间的方法. 硬件时间:hwclock 或者clock,设置的方法是 hwclock --set --date="05/12/2018 12:30:50 ...