洛谷 P4516 [JSOI2018]潜入行动
一眼树形 \(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):
- 若合并两个大小 \(>k\) 的子树,由于这样的子树最多 \(\frac{n}{k}\) 个,暴力合并复杂度是 \(nk\) 的。
- 若合并一棵大小 \(\leq k\) 的和一棵大小 \(>k\) 的子树,这样那个大小 \(\leq k\) 的子树就变成了大小 \(>k\) 的子树。由于每个点最多只在它的某个祖先处被合并一次,这样复杂度均摊也是 \(nk\) 的。
- 若合并两棵大小 \(\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]潜入行动的更多相关文章
- 洛谷P4559 [JSOI2018]列队 【70分二分 + 主席树】
题目链接 洛谷P4559 题解 只会做\(70\)分的\(O(nlog^2n)\) 如果本来就在区间内的人是不用动的,区间右边的人往区间最右的那些空位跑,区间左边的人往区间最左的那些空位跑 找到这些空 ...
- luogu P4516 [JSOI2018]潜入行动
LINK:潜入行动 初看题感觉很不可做 但是树形dp的状态过于明显. 容易设\(f_{x,j,l,r}\)表示x为根子树内放了j个设备且子树内都被覆盖l表示x是否被覆盖r表示x是否放设备的方案数. 初 ...
- 洛谷P4518 [JSOI2018]绝地反击(计算几何+二分图+退流)
题面 传送门 题解 调了咱一个上午-- 首先考虑二分答案,那么每个点能够到达的范围是一个圆,这个圆与目标圆的交就是可行的区间,这个区间可以用极角来表示 首先,如果我们知道这个正\(n\)边形的转角,也 ...
- 洛谷P4517 [JSOI2018]防御网络(dp)
题面 传送门 题解 翻译一下题意就是每次选出一些点,要用最少的边把这些点连起来,求期望边数 我也不知道为什么反正总之就是暴力枚举太麻烦了所以我们考虑贡献 如果一条边是割边,那么它会在图里当且仅当两边的 ...
- 洛谷P4557 [JSOI2018]战争(闵可夫斯基和+凸包)
题面 传送门 题解 看出这是个闵可夫斯基和了然而我当初因为见到这词汇是在\(shadowice\)巨巨的\(Ynoi\)题解里所以压根没敢学-- 首先您需要知道这个 首先如果有一个向量\(w\)使得\ ...
- 洛谷P4559 [JSOI2018]列队(主席树)
题面 传送门 题解 首先考虑一个贪心,我们把所有的人按\(a_i\)排个序,那么排序后的第一个人到\(k\),第二个人到\(k+1\),...,第\(i\)个人到\(k+i-1\),易证这样一定是最优 ...
- 洛谷 P4559: bzoj 5319: [JSOI2018]军训列队
题目传送门:洛谷 P4559. 题意简述: 有 \(n\) 个学生,编号为 \(i\) 的学生有一个位置 \(a_i\). 有 \(m\) 个询问,每次询问编号在 \([l,r]\) 区间内的学生跑到 ...
- 【BZOJ5314】[JSOI2018]潜入行动(动态规划)
[BZOJ5314][JSOI2018]潜入行动(动态规划) 题面 BZOJ 洛谷 题解 不难想到一个沙雕\(dp\),设\(f[i][j][0/1][0/1]\)表示当前点\(i\),子树中一共放了 ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
随机推荐
- Java 读取PDF中的表格
一.概述 本文以Java示例展示读取PDF中的表格的方法.这里导入Spire.PDF for Javah中的jar包,并使用其提供的相关及方法来实现获取表格中的文本内容.下表中整理了本次代码使用到的主 ...
- try-catch-finally面试题
try catch finally 执行顺序面试题总结 执行顺序 今天牛客网遇到这个题目,做对了,但是下面的评论却很值得看看 public class TestTry { public int add ...
- elasticsearch的索引操作
1.创建索引(test_index) curl -XPUT "http://192.168.99.1:9200/test_index" 2.创建索引,指定分片和副本的数量 curl ...
- uvm_subscriber
subscriber是消费,用户的意思 uvm_subscriber主要作为coverage的收集方式之一 uvm_subscriber的代码非常简单,继承于uvm_component,再加上一个an ...
- hdu 5178 pairs(BC第一题,,方法不止一种,,我用lower_bound那种。。。)
题意: X坐标上有n个数.JOHN想知道有多少对数满足:x[a]-x[b]<=k(题意给)[a<b] 思路: 额,,,直接看代码吧,,,, 代码: int T,n,k; int x[100 ...
- oeasy教您玩转vim - 56 - # 字符可视化模式
可视化编辑 回忆上节课内容 我们学习了关于模式匹配中使用参数 单个参数 :%s/<h2>\(.*\)</h2>/ - \1/g 多个参数 :%s/<img src=\ ...
- JavaScript 简单介绍
一.简介 JavaScript是一门面向对象的动态语言,他一般用来处理以下任务: 修饰网页 生成HTML和CSS 生成动态HTML内容 生成一些特效 提供用户交互接口 生成用户交互组件 验证用户输入 ...
- 『学了就忘』Linux基础命令 — 31、grep命令和通配符
目录 1.grep命令介绍 2.find命令和grep命令的区别(重点) (1)find命令 (2)grep命令 3.通配符与正则表达式的区别 (1)通配符: (2)正则表达式: 1.grep命令介绍 ...
- C# 合并两个数组总结
byte[] b1 = new byte[] { 1, 2, 3, 4, 5 }; byte[] b2 = new byte[] { 6, 7, 8, 9 }; byte[] b3 = new byt ...
- Python科普系列——类与方法(上篇)
欢迎来到新的系列,up又开新坑了~~ 实际上,Python作为一门易用性见长的语言,看上去简单,却仍然有很多值得一说的内容,因此这个系列会把Python中比较有意思的地方也给科普一遍.而另一方面,关于 ...