原文链接 https://www.cnblogs.com/cly-none/p/9845046.html

题意:给出一棵\(n\)个结点的树,在第\(i\)个结点上有\(a_i\)个权值为\(v_i\)的物品。\(1\)号结点是根结点。你需要选出若干个物品(设选了\(t\)个),满足:

  • 如果选了结点\(i\)上的物品,那么\(i\)到根的链上每个结点都至少要选一个物品。
  • 设有选取物品的结点的最大深度为\(h\),那么\(t \leq h + k\),\(k\)为一个给定的常数。

在此基础上,你需要最大化所选的物品的权值和。

\(n \leq 2 \times 10^4, \, k \leq 5 \times 10^5, \, n \times k \leq 2.5 \times 10^7\)

显然,最终做法的复杂度应该是\(O(nk)\)的。

但这个问题比较复杂,直接想比较困难。因此,我们先考虑问题的简化版。

问题1

当第二个条件改为\(t \leq k\)时,怎么做?

对于这种一个结点的决策影响其子树的问题,我们可以对dfs序倒过来dp。确切地说,考虑当前是\(i\),那么\(i\)的子树就是\(dfn_i\)之后的一段连续区间。那么,把dfs序倒过来后,结点\(i\)就有两种可能:

  • 选了\(i\)上的物品。就是一个多重背包,从\(dp_{dfn_i + 1}\)上更新过来。
  • 不选\(i\)上的物品。那\(i\)子树中的所有物品都不能选。从\(dp_{dfn_i + sz_i}\)上更新过来。

用单调队列优化多重背包后,就能做到\(O(nk)\)。


然而,回过头来,我们依旧对\(t \leq h + k\)感到棘手。尝试按常规方法dp对\(k+h-t\)记录答案,但没有用。这个限制其实就在于,选出一条一段是根结点的链,链上每个点都取一个不计入\(t\)的物品。我们设这条链除\(1\)外的端点为\(x\)。考虑\(\forall i, \, a_i = 1\)的部分分。那么,假如我们已经确定了\(x\),则剩下的答案就是删去\(1\)到\(x\)的链,对剩下的森林做问题1的结果。

因此,我们可以考虑下面这个问题:

问题2

预处理:对于所有\(x\),删去\(x\)到根的路径后剩下的森林的问题1的答案。

博主认为,这个问题的解法相当有趣,也挺难想到的。

考虑剩下的森林的一半就是在dfs序上,从\(dfn_i + 1\)到\(n\)的一段区间(包括了\(i\)的子树)。这个部分我们在dp时就已经把答案求出来了。然而,另一部分在dfs序上既不是一段后缀,也不是连续的区间。\([1,dfn_i-1]\)中还混入了\(i\)的所有祖先。

因此,我们把这棵树左右翻转,把剩下森林的两半交换位置。也就是,再生成一个dfs序,但每个结点反序访问它的孩子结点。这样,我们就把森林的另一部分也表示为了dfs序的一个后缀。值得注意的是,\(i\)的子树不能算两次,所以这个后缀应该是[dfn_i + sz_i,n]。

这样,我们做出两个dfs序,对每个做问题1的dp,就能解决此问题。


然后就是处理\(a_i \neq 1\)的情况。上面的算法会错误,就在于\(x\)到根的路径上的结点,可能选了多个物品。那么,我们就对每个结点\(i\)建一个辅助点\(i'\),存放了\(a_i - 1\)个原来在\(i\)上的物品。这样,对于任何一个非辅助结点,它到根的路径上所有点都只有一个物品。

这样就能把最终问题转化为问题1,\(O(nk)\)地解决本题。

#include <bits/stdc++.h>
using namespace std;
const int N = 40010, K = 500010, SIZE = 51000010;
int n,k,val[N],num[N],dfn[N],sz[N],fa[N],cnt,dis[N],ans,rec[N],spadp[SIZE],spag[SIZE];
vector<int> ch[N];
int *dp[N],*g[N];
void dfs(int pos) {
sz[pos] = 1;
for (int i = 0 ; i < (int)ch[pos].size() ; ++ i) {
dfs(ch[pos][i]);
sz[pos] += sz[ch[pos][i]];
}
dfn[rec[pos] = ++cnt] = pos;
}
void fsd(int pos) {
dis[pos] += val[pos];
for (int i = (int)ch[pos].size() - 1 ; i >= 0 ; -- i) {
dis[ch[pos][i]] = dis[pos];
fsd(ch[pos][i]);
}
dfn[++cnt] = pos;
}
void update(int las,int cur) {
static int q[K],l,r;
l = 1, r = 0;
q[++r] = 0;
for (int i = 1 ; i <= k ; ++ i) {
while (l <= r && i - q[l] > num[dfn[cur]])
++ l;
if (l <= r)
dp[cur][i] = dp[las][q[l]] + val[dfn[cur]] * (i - q[l]);
else dp[cur][i] = 0;
while (l <= r && dp[las][i] > dp[las][q[r]] + val[dfn[cur]] * (i - q[r]))
-- r;
q[++r] = i;
}
}
void init() {
ans = 0;
for (int i = 0 ; i <= 2 * n ; ++ i) {
ch[i].clear();
dp[i] = spadp + i * (k + 1);
g[i] = spag + i * (k + 1);
memset(dp[i],0,sizeof(int) * (k + 1));
memset(g[i],0,sizeof(int) * (k + 1));
}
dis[1] = 0;
}
int main() {
int T;
scanf("%d",&T);
while (T --) {
scanf("%d%d",&n,&k);
init();
for (int i = 1 ; i <= n ; ++ i)
scanf("%d%d%d",&fa[i],&num[i],&val[i]);
for (int i = 2 ; i <= n ; ++ i)
ch[fa[i]].push_back(i);
for (int i = 1 ; i <= n ; ++ i) {
ch[i].push_back(i+n);
val[i+n] = val[i];
num[i+n] = num[i] - 1;
num[i] = 1;
}
cnt = 0;
dfs(1);
for (int i = 1 ; i <= 2 * n ; ++ i) {
update(i-1,i);
for (int j = 1 ; j <= k ; ++ j)
dp[i][j] = max(dp[i][j],dp[i - sz[dfn[i]]][j]), dp[i][j] = max(dp[i][j],dp[i][j-1]);
}
for (int i = 1 ; i <= 2 * n ; ++ i)
for (int j = 1 ; j <= k ; ++ j)
g[i][j] = dp[i][j];
cnt = 0;
fsd(1);
for (int i = 1 ; i <= 2 * n ; ++ i) {
update(i-1,i);
for (int j = 1 ; j <= k ; ++ j)
dp[i][j] = max(dp[i][j],dp[i - sz[dfn[i]]][j]), dp[i][j] = max(dp[i][j],dp[i][j-1]);
}
for (int i = 1 ; i <= 2 * n ; ++ i) {
if (dfn[i] > n) continue;
int p = rec[dfn[i]] - sz[dfn[i]];
for (int j = 0 ; j <= k ; ++ j)
ans = max(ans,dis[dfn[i]] + dp[i-1][j] + g[p][k-j]);
}
printf("%d\n",ans);
}
return 0;
}

小结:一道对dfs序上dp进行拓展的好题。当一个问题分成了性质相同的两半,而前者容易解决,后者难以解决的问题时,寻找方式来交换这两部分的位置,最后合并。这个思路应该记住。

【做题】SDOI2017苹果树——dfs序的运用的更多相关文章

  1. CODEVS.1228 苹果树(DFS序)

    To CODEVS.1228 苹果树  To poj 3321 Description 在卡卡的房子外面,有一棵苹果树.每年的春天,树上总会结出很多的苹果.卡卡非常喜欢吃苹果,所以他一直都精心的呵护这 ...

  2. poj3321 dfs序+树状数组单点更新 好题!

    当初听郭炜老师讲时不是很懂,几个月内每次复习树状数组必看的题 树的dfs序映射在树状数组上进行单点修改,区间查询. /* 树状数组: lowbit[i] = i&-i C[i] = a[i-l ...

  3. hdu 5692(dfs序+线段树,好题)

    Snacks Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  4. BZOJ2434[Noi2011]阿狸的打字机——AC自动机+dfs序+树状数组

    题目描述 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小 ...

  5. BZOJ 3252题解(贪心+dfs序+线段树)

    题面 传送门 分析 此题做法很多,树形DP,DFS序+线段树,树链剖分都可以做 这里给出DFS序+线段树的代码 我们用线段树维护到根节点路径上节点权值之和的最大值,以及取到最大值的节点编号x 每次从根 ...

  6. poj3321-Apple Tree(DFS序+树状数组)

    Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 36442   Accepted: 10894 Desc ...

  7. cf276E 两棵线段树分别维护dfs序和bfs序,好题回头再做

    搞了一晚上,错了,以后回头再来看 /* 对于每次更新,先处理其儿子方向,再处理其父亲方向 处理父亲方向时无法达到根,那么直接更新 如果能达到根,那么到兄弟链中去更新,使用bfs序 最后,查询结点v的结 ...

  8. (好题)树状数组+离散化+DFS序+离线/莫队 HDOJ 4358 Boring counting

    题目传送门 题意:给你一棵树,树上的每个节点都有树值,给m个查询,问以每个点u为根的子树下有多少种权值恰好出现k次. 分析:首先要对权值离散化,然后要将树形转换为线形,配上图:.然后按照右端点从小到大 ...

  9. 【NOI2019集训题2】 序列 后缀树+splay+dfs序

    题目大意:给你一个长度为$n$的序列$a_i$,还有一个数字$m$,有$q$次询问 每次给出一个$d$和$k$,问你对所有的$a_i$都在模$m$意义下加了$d$后,第$k$小的后缀的起点编号. 数据 ...

随机推荐

  1. 2017-2018 ACM-ICPC Latin American Regional Programming Contest GYM101889

    挺有意思的一套题,题也没有啥毒瘤了,本来是队切的结果种种原因大家全挂机了. 只补了百人题,一共7个,其他的暂时先不补了,,也不会嘛qwq H:签到 #include <bits/stdc++.h ...

  2. ubuntu 忽略文件的50unattended升级问题

    ubuntu出现这样问题 既然说那个文件扩展名无效,那干脆直接把那个文件删掉 sudo rm /etc/apt/apt.conf.d/20auto-upgrades.ucf-old 删掉之后应该就不会 ...

  3. 【登录异常解决】Ubuntu 输入正确的密码后重新返回到登陆界面

    症状 Ubuntu 输入正确的密码后,黑屏一闪,重新返回到登陆界面. 原因一:主目录下的.Xauthority文件拥有者变成了root,从而以用户登陆的时候无法都取.Xauthority文件.说明:X ...

  4. 10、DOM(文档对象模型)

    1.认识DOM html    骨架 css     装修 javascript 物业 ==DOM 打破上述三者的通道.== [注]script标签一般情况下要写在head标签. <div id ...

  5. position inherit 定位

    inherit  继承父元素   定位 举例  : <div class="father"> <p></p> </div> div{ ...

  6. 三、CSS语言

    CSS语言 1.概述:CSS (Cascading Style Sheets)是层叠样式表用来定义网页的显示效果.可以解决html代码对样式定义的重复,提高了后期样式代码的可维护性,并增强了网页的显示 ...

  7. js中级小知识1

    首先我们复习之前的小知识,本期博客与之前有关 js数据类型 基本数据类型:string    undefined         null         boolean          numbe ...

  8. VLAN之间通信-三层交换

    实验目的 VLAN之间通信-三层交换 掌握配置VLANIF接口的方法 理解数据包跨VLAN路由的原理 掌握测试多层交换网络连通性的方法 实验原理 三层交换机在原有二层交换机的基础之上增加了路由功能,同 ...

  9. ng2-tree

    [转]https://github.com/valor-software/ng2-tree#eyes-demo  demo:http://valor-software.com/ng2-tree/

  10. SharePoint开启错误提示

    1,打开80下面的Web.config文件2,CallStack="true" 和 <customErrors mode="Off" /> < ...