Codeforces 题面传送门 & 洛谷题面传送门

显然我们选择删除的点连同 \(u\) 会形成一个连通块,否则我们如果选择不删除不与 \(u\) 在同一连通块中的点,答案一定更优。

注意到如果我们选择删除 \(u\) 的某个儿子 \(v\),那么答案的增量为 \(chd_v-1-k\),其中 \(chd_v\) 为节点 \(v\) 儿子的个数。而初始时刻答案为 \(chd_u\) 是个定值,因此我们的任务可以等效于,给每个点赋上一个点权 \(chd_u-1-k\),然后要找到一个以 \(u\) 为根的树上连通块,使得这个连通块中除去点 \(u\) 之外其他点点权之和尽可能大。

因此我们有了一个复杂度 \(\Theta(n^2)\) 的做法:对于每一个 \(k\) 都跑一遍树形 \(dp\),即设 \(dp_u\) 表示以 \(u\) 为根的连通块点权之和的最大值,那么显然有转移 \(dp_u=chd_u-1-k+\sum\limits_{v\in son_u}\max(dp_v,0)\)。注意到我们选出的树上连通块边界上(也就是所有满足 \(x\) 在树上连通块中,但 \(x\) 的所有儿子都不在树上连通块)中的点的点权必定都是正的,否则我们扣掉那些点权非正且在连通块边界上的点,答案肯定变得更优。因此考虑对于每个 \(k\) 将所有点权为正的点拎出来建一棵虚树——由于 \(\sum\limits_{i=1}^nchd_i=n-1\),所以对于所有 \(k\),点权为正的点的总个数是 \(\Theta(n)\) 的,然后对它们跑树形 \(dp\),然后每次查询一个点 \(u\) 为根的连通块点权之和的最大值时,如果点 \(u\) 本身就在虚树上,直接返回 \(u\) 的 DP 值即可,否则我们找到 \(u\) 在虚树上所在的链下方的节点 \(v\)——这个可以拿个 set 存储所有在虚树上的节点的 DFS 序,然后直接在 setlower_bound。那么以 \(u\) 为根的连通块点权和的最大值就是 \(dp_v\) 加上 \(fa_v\to u\) 这段路径和的点权和。

时间复杂度 \(\Theta(n\log n)\)​​。

  1. const int MAXN=2e5;
  2. const int LOG_N=18;
  3. int n,qu,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
  4. void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
  5. int chd[MAXN+5],dep[MAXN+5],fa[MAXN+5][LOG_N+2];
  6. int dfn[MAXN+5],tim=0,rid[MAXN+5],edt[MAXN+5];
  7. void dfs0(int x,int f){
  8. fa[x][0]=f;rid[dfn[x]=++tim]=x;
  9. for(int e=hd[x];e;e=nxt[e]){
  10. int y=to[e];if(y==f) continue;
  11. chd[x]++;dep[y]=dep[x]+1;dfs0(y,x);
  12. } edt[x]=tim;
  13. }
  14. int sum_chd[MAXN+5];
  15. void dfs_chd(int x,int f){
  16. for(int e=hd[x];e;e=nxt[e]){
  17. int y=to[e];if(y==f) continue;
  18. sum_chd[y]=sum_chd[x]+chd[y];
  19. dfs_chd(y,x);
  20. }
  21. }
  22. void lca_init(){
  23. dep[1]=1;dfs0(1,0);
  24. for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++)
  25. fa[j][i]=fa[fa[j][i-1]][i-1];
  26. }
  27. int getlca(int x,int y){
  28. if(dep[x]<dep[y]) swap(x,y);
  29. for(int i=LOG_N;~i;i--) if(dep[x]-(1<<i)>=dep[y]) x=fa[x][i];
  30. if(x==y) return x;
  31. for(int i=LOG_N;~i;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  32. return fa[x][0];
  33. }
  34. vector<int> pos[MAXN+5];
  35. int stk[MAXN+5],tp=0;set<int> st;
  36. vector<pii> qv[MAXN+5];
  37. ll calc_sum(int u,int v,int k){//u is ancestor of v
  38. return (sum_chd[v]-sum_chd[u])-1ll*(k+1)*(dep[v]-dep[u]);
  39. }
  40. int calc_val(int u,int k){return chd[u]-1-k;}
  41. vector<int> g[MAXN+5];
  42. ll dp[MAXN+5],res[MAXN+5];
  43. void insert(int x){
  44. if(!tp) return stk[++tp]=x,void();
  45. int lc=getlca(x,stk[tp]);
  46. // printf("LCA of %d %d is %d\n",x,stk[tp],lc);
  47. // printf("stack: ");
  48. // for(int i=1;i<=tp;i++) printf("%d%c",stk[i]," \n"[i==tp]);
  49. while(tp>1&&dep[stk[tp-1]]>dep[lc]){
  50. // printf("adde %d %d\n",stk[tp-1],lc);
  51. g[stk[tp-1]].pb(stk[tp]);--tp;
  52. }
  53. if(tp&&dep[stk[tp]]>dep[lc]){
  54. // printf("adde %d %d\n",lc,stk[tp]);
  55. g[lc].pb(stk[tp--]);
  56. }
  57. if(!tp||stk[tp]!=lc) stk[++tp]=lc;
  58. stk[++tp]=x;
  59. }
  60. void fin(){
  61. while(tp>=2){
  62. // printf("adde %d %d\n",stk[tp-1],stk[tp]);
  63. g[stk[tp-1]].pb(stk[tp]);--tp;
  64. } tp=0;
  65. }
  66. void dfs_dp(int x,int k){
  67. dp[x]=calc_val(x,k);st.insert(dfn[x]);
  68. for(int y:g[x]){
  69. dfs_dp(y,k);
  70. dp[x]+=max(dp[y]+calc_sum(x,y,k)-calc_val(y,k),0ll);
  71. // printf("calc_sum %d %d %d = %lld\n",x,y,k,calc_sum(x,y,k));
  72. } //printf("DP %d %lld\n",x,dp[x]);
  73. }
  74. void clr(int x){for(int y:g[x]) clr(y);g[x].clear();}
  75. int main(){
  76. scanf("%d",&n);
  77. for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
  78. lca_init();sum_chd[1]=chd[1];dfs_chd(1,0);scanf("%d",&qu);
  79. // for(int i=1;i<=n;i++) printf("chd[%d]=%d\n",i,chd[i]);
  80. for(int i=1;i<=n;i++) for(int j=0;j<chd[i];j++) pos[j].pb(i);
  81. for(int i=1,u,k;i<=qu;i++) scanf("%d%d",&u,&k),qv[k].pb(mp(u,i));
  82. for(int i=0;i<=MAXN;i++){
  83. st.clear();
  84. if(!pos[i].empty()){
  85. // printf("solving %d\n",i);
  86. sort(pos[i].begin(),pos[i].end(),[&](int x,int y){return dfn[x]<dfn[y];});
  87. if(pos[i][0]!=1) insert(1);
  88. for(int x:pos[i]) insert(x);
  89. fin();dfs_dp(1,i);
  90. }
  91. for(pii p:qv[i]){
  92. int u=p.fi,id=p.se;
  93. set<int>::iterator it=st.lower_bound(dfn[u]);
  94. if(it==st.end()||(*it)>edt[u]) res[id]=chd[u];
  95. else{
  96. int pt=rid[*it];
  97. // printf("%d %d\n",id,pt);
  98. // printf("%lld\n",calc_sum(fa[u][0],fa[pt][0],i));
  99. res[id]=chd[u]+max(0ll,dp[pt]+calc_sum(fa[u][0],fa[pt][0],i)-calc_val(u,i));
  100. }
  101. }
  102. clr(1);
  103. }
  104. for(int i=1;i<=qu;i++) printf("%lld\n",res[i]);
  105. return 0;
  106. }
  107. /*
  108. 11
  109. 1 2
  110. 2 3
  111. 2 4
  112. 1 5
  113. 5 6
  114. 5 7
  115. 7 8
  116. 7 9
  117. 7 10
  118. 1 11
  119. 4
  120. 1 0
  121. 1 1
  122. 5 2
  123. 11 0
  124. */

Codeforces 1606F - Tree Queries(虚树+树形 dp)的更多相关文章

  1. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  2. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  3. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  4. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  5. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  6. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

  7. 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)

    传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...

  8. CodeCraft-19 and Codeforces Round #537 (Div. 2) E 虚树 + 树形dp(新坑)

    https://codeforces.com/contest/1111/problem/E 题意 一颗有n个点的树,有q个询问,每次从树挑出k个点,问将这k个点分成m组,需要保证在同一组中不存在一个点 ...

  9. Codeforces 1111 E. Tree(虚树,DP)

    题意 有一棵树,q个询问,每次询问,指定一个点做树根,再给定k个点,要求把这些点分成不超过m组的方案数,分配的限制是任意两个有祖先关系的点不能分在同一组.题目还保证了所有的询问的k加起来不超过1e5. ...

随机推荐

  1. Python技法3:匿名函数、回调函数和高阶函数

    1.定义匿名或内联函数 如果我们想提供一个短小的回调函数供sort()这样的函数用,但不想用def这样的语句编写一个单行的函数,我们可以借助lambda表达式来编写"内联"式的函数 ...

  2. QG-2019-AAAI-Improving Neural Question Generation using Answer Separation

    Improving Neural Question Generation using Answer Separation 本篇是2019年发表在AAAI上的一篇文章.该文章在基础的seq2seq模型的 ...

  3. relativeLayout相对布局的嵌套在py中的引用

    from kivy.app import App from kivy.uix.button import Button from kivy.uix.relativelayout import Rela ...

  4. 热身 for computer industry

    项目 内容 作业属于 班级博客 作业要求 作业要求 个人课程目标 掌握软件工程基础知识 具体有助方面 个人认知与规划 其他参考文献 博客Ⅰ 博客 Ⅱ 选择计算机 你为什么选择计算机专业?你认为你的条件 ...

  5. 2021.9.28考试总结[NOIP模拟64]

    T1 三元组 发现确定\(b,c\)的情况下,\(a\)的值域是连续的.确定\(b\)后\(a+b\)的取值是\([1+b,b+b]\).树状数组维护对每个\(b\)可行的\(c\). 注意取模后取值 ...

  6. 按照工业标准1英寸=25.4mm,而在电子元件成像领域Sensor尺寸1英寸=16mm。

    按照工业标准1英寸=25.4mm,而在电子元件成像领域Sensor尺寸1英寸=16mm. 我们平常所说的CCD/CMOS的尺寸,实际上是指Sensor对角线的长度,这一点跟我们平常所说的屏幕尺寸是一样 ...

  7. stm32看门狗详细解答,看了觉得一下子明白了很多

    一.独立看门狗 STM32 的独立看门狗由内部专门的 40Khz 低速时钟驱动,即使主时钟发生故障,它也仍然有效. 看门狗的原理:单片机系统在外界的干扰下会出现程序跑飞的现象导致出现死循环,看门狗电路 ...

  8. 绘制PCB电路原理图的8种方法

    1.选择集成电路,变压器,晶体管等组件,这些组件体积庞大,有许多引脚并在电路中起主要作用,然后从选定的参考引脚中抽取,以减少错误. 2.如果PCB上标有元件编号(如VD870,R330,C466等), ...

  9. GDI+图形图像技术1

    System.Drawing命名空间提供了对GDI+基本图形功能的访问,其中一些子命名空间中提供了更高级的功能. GDI+由GDI发展而来,是Windows图形显示程序与实际物理设备之间的桥梁. GD ...

  10. 释放 cached 内存

    巡检服务器发现内存可用很少了 top 命令查看是没有占用大内存的进程,cached特别大,释放cached就可以了 可用内存= free + buffers + cached 以下方法可以释放cach ...