题面传送门

一眼树形 \(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. 如何再一台电脑上配置多个tomcat同时运行

    1.配置运行tomcat 首先要配置java的jdk环境,这个就不在谢了  不懂去网上查查,这里主要介绍再jdk环境没配置好的情况下 如何配置运行多个tomcat 2.第一个tomcat: 找到&qu ...

  2. OO电梯作业总结

    (一)第五次作业 一.设计思路 生产消费者模型,输入接口是producer,调度器是tray,电梯是customer.由于只有一架电梯,所以生产消费模型满足以下条件: 一个生产者,一个消费者 托盘不为 ...

  3. js基础学习之"=="与"==="的区别

    var a = 1; var b = 1; var c = "1"; 1. "==" 可理解为相等运算符.相等运算符比较时,会自己进行类型转换,等于什么类型就会 ...

  4. C语言中都有哪些常见的数据结构你都知道几个??

    上次在面试时被面试官问到学了哪些数据结构,那时简单答了栈.队列/(ㄒoㄒ)/~~其它就都想不起来了,今天有空整理了一下几种常见的数据结构,原来我们学过的数据结构有这么多~ 首先,先来回顾下C语言中常见 ...

  5. 21.10.9 test

    T1 购票方案 \(\color{green}{100}\) 对于每个时间节点维护它作为每种票所能包含的最后一个点时,这种票的起始点位置,由于这个位置是单调的,所以类似双指针维护,\(O(KN)\) ...

  6. SQL Server 插入、更新和删除数据

    1.主要内容 ● 通过SSMS,插入.更新和删除表数据 ● 通过INSERT语句向表中插入数据 ● 通过UPDATE语句更新表内数据 ● 通过DELETE语句删除表内数据 ● 使用INSERT.UPD ...

  7. 全志Linux Tina编译demoOmxVdec错误

    测试裸流 Making install in demoOmxVdec make[6]: Entering directory '/home/liuxueneng/WorkCode/Homlet-Tin ...

  8. Python3 TypeError: initial_value must be str or None, not bytes

    response.read() returns an instance of bytes while StringIO is an in-memory stream for text only. Us ...

  9. 0x04

    二分: while(l<r) { int mid=(l+r)/2; if(符合条件) r=mid; else l=mid+1; } 固定下二分的写法: 终止条件:l==r: 取mid=(l+r) ...

  10. JMeter学习笔记--录制脚本(二)

    第一步:在JMeter中添加线程组,命名为访问首页 第二步:在线程组下添加HTTP请求默认值 添加->配置元件->HTTP请求默认值,设置服务器IP和端口号(JMeter默认使用80端口号 ...