http://www.lydsy.com/JudgeOnline/problem.php?id=2286 (题目链接)

一个小小的细节,WA了一天,欲哭无泪了。。

题意

  给出一个n个节点的带权树,总共m次询问,每次询问给出K个节点标号,求出切断这些节点与1号节点的路径的最少花费。

solution

  构造虚数+树形dp。

  首先,有关虚树的题有一个特征,就是题目会给出sigema(k[i])的范围,保证不会太大。所以我们考虑对于每一次询问构造一棵虚树,然后再在虚树上跑dp,可以大大减少复杂度,比如u,v两点之间没有其他询问点,那么我们就可以把uv直接连起来,中间的点是什么我们并不关心。我们只要建出这样一棵树即可:只含当前询问点和它们的lca,并且相对位置关系不变。 
举个例子,比如说选1,5,8,10号节点。 

  那么构造出来的的虚树就是: 

  那么如何构造虚树呢?

  我们先对原树dfs一遍,预处理出dfs序(mark[]),将询问点按照dfs序排序,依次插入一个栈,来动态维护一个叫做“最右链”的东西,就是最右边的一条链……这个也不太好说明,最好自己看着代码模拟一遍……

  用虚树还要满足一个条件,就是要维护的信息。例如,和,最大,最小,都有类似于前缀和的性质。就是我们可以从v(u的后代)直接求出u的答案,而不需要遍历u到v的所有边,否则虚树就没有降低复杂度,因为每次还是要在原树上走。在这道题中,我们用一个数组mn[i]来维护节点i到根节点1的路径上的花费最小的那一条边权,mn[i]我们可以在dfs时预处理出。这有什么用呢,看下面。

  关于如何在虚树上dp,我么有了mn[]数组后,就变的很简单,f[i]表示断开询问点i以及i的子树上的询问点到根节点1的路径的最小花费。转移方程:f[i]=min(mn[i],sigema(f[e[i].to]),其中e[i].to指的是i节点的孩子节点。

  可是我们考虑一种情况,借用上面的图1,若询问点是2和8,mn[8]=4->8=1,mn[2]=1->2=4,按照我们的算法,那么得出的答案就会是1,而这样是不正确的。

  也就是说,当存在询问点u,v设deep[u]<deep[v]使lca(u,v)==u时,我们的dp方程是不成立的。对于这种情况,选择切断深度浅的点的mn[u]是最优的。想一想,若切断了mn[u],那么在i的子树上的询问点v自然也被切断了。

  所以我们在构建虚树的时候,就预先将这种情况处理掉,也就是在u,v中只选择u放入虚树中。所以虚树中只有叶子节点是询问点。

细节

  题目数据范围有误→_→。最后输出的答案可能会很大,记得开LL。inf要开到很大,2147483647是远远不够的(博主就这样WA了一天= =)。还有邻接表头head[]不能直接memset。

代码

  1. // bzoj2286
  2. #include<algorithm>
  3. #include<iostream>
  4. #include<cstdlib>
  5. #include<cstring>
  6. #include<cstdio>
  7. #include<cmath>
  8. #define LL long long
  9. #define inf (1ll<<60)
  10. #define Pi acos(-1.0)
  11. #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
  12. using namespace std;
  13.  
  14. const int maxn=300010;
  15. int a[maxn],deep[maxn],head[maxn],dfn[maxn],fa[maxn][30],bin[30],s[maxn];
  16. int n,cnt,Q,K;
  17. LL f[maxn],mn[maxn];
  18. struct edge {int to,next;LL w;}e[maxn<<1];
  19.  
  20. bool cmp(int a,int b) {
  21. return dfn[a]<dfn[b];
  22. }
  23. void link(int u,int v,LL w) {
  24. e[++cnt]=(edge){v,head[u],w};head[u]=cnt;
  25. e[++cnt]=(edge){u,head[v],w};head[v]=cnt;
  26. }
  27. void link(int u,int v) {
  28. e[++cnt]=(edge){v,head[u],0};head[u]=cnt;
  29. }
  30. void dfs(int x) {
  31. dfn[x]=++cnt;
  32. for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
  33. for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
  34. mn[e[i].to]=min(mn[x],e[i].w);
  35. fa[e[i].to][0]=x;
  36. deep[e[i].to]=deep[x]+1;
  37. dfs(e[i].to);
  38. }
  39. }
  40. int lca(int x,int y) {
  41. if (deep[x]<deep[y]) swap(x,y);
  42. int t=deep[x]-deep[y];
  43. for (int i=0;bin[i]<=t;i++) if (bin[i]&t) x=fa[x][i];
  44. for (int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  45. return x==y ? x : fa[x][0];
  46. }
  47. void build() {
  48. cnt=0;int top=1,tot=1;
  49. for (int i=2;i<=K;i++) if (lca(a[tot],a[i])!=a[tot]) a[++tot]=a[i];
  50. s[top]=1;
  51. for (int i=1;i<=tot;i++) {
  52. int x;
  53. while (top) {
  54. x=lca(a[i],s[top]);
  55. if (top>1 && deep[x]<deep[s[top-1]]) link(s[top-1],s[top]),top--;
  56. else if (deep[x]<deep[s[top]]) {link(x,s[top--]);break;}
  57. else break;
  58. }
  59. if (s[top]!=x) s[++top]=x;
  60. s[++top]=a[i];
  61. }
  62. while (--top) link(s[top],s[top+1]);
  63. }
  64. void dp(int x) {
  65. LL tmp=0;f[x]=mn[x];
  66. for (int i=head[x];i;i=e[i].next) {
  67. dp(e[i].to);
  68. tmp+=f[e[i].to];
  69. }
  70. head[x]=0; //注意只能在这里清空邻接表,不然复杂度不对
  71. f[x]=tmp==0 ? mn[x] : min(tmp,mn[x]);
  72. }
  73. int main() {
  74. bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
  75. scanf("%d",&n);
  76. for (int u,v,w,i=1;i<n;i++) {
  77. scanf("%d%d%d",&u,&v,&w);
  78. link(u,v,w);
  79. }
  80. cnt=0;mn[1]=inf;dfs(1);
  81. scanf("%d",&Q);
  82. memset(head,0,sizeof(head));
  83. for (int i=1;i<=Q;i++) {
  84. scanf("%d",&K);
  85. for (int j=1;j<=K;j++) scanf("%d",&a[j]);
  86. sort(a+1,a+1+K,cmp);
  87. build();
  88. dp(1);
  89. printf("%lld\n",f[1]);
  90. }
  91. return 0;
  92. }

  

【bzoj2286】 消耗战的更多相关文章

  1. bzoj2286 消耗战

    还是虚树的题目啊... 如果只有一个询问,我们这么考虑,可以设dp[x]为只删除x子树内和x到父亲的边,使得x这棵子树内的能源岛屿都与x的父亲不连通的最小花费. 这样如果x本身是能源岛屿,那么dp[x ...

  2. [Bzoj2286]消耗战(虚树+DP)

    Description 题目链接 Solution 在虚树上跑DP即可 Code #include <cstdio> #include <algorithm> #include ...

  3. [SDOI2011][bzoj2286] 消耗战 [虚树+dp]

    题面: 传送门 思路: 看到所有询问中的点数总和是十万级别的,就想到用虚树~\(≧▽≦)/~啦 首先,树形dp应该是很明显可以看出来的: 设dp[u]表示以u为根的子树(不包括u)中的宝藏岛全部切断的 ...

  4. [bzoj2286][Sdoi 2011]消耗战

    [bzoj2286]消耗战 标签: 虚树 DP 题目链接 题解 很容易找出\(O(mn)\)的做法. 只需要每次都dp一遍. 但是m和n是同阶的,所以这样肯定会T的. 注意到dp的时候有很多节点是不需 ...

  5. DP——由蒟蒻到神犇的进阶之路

    开始更新咯 DP专题[题目来源BZOJ] 一.树形DP 1.bzoj2286消耗战 题解:因为是树形结构,一个点与根节点不联通,删一条边即可, 于是我们就可以简化这棵树,把有用的信息建立一颗虚树,然后 ...

  6. 【BZOJ2286】消耗战(虚树,动态规划)

    [BZOJ2286]消耗战(虚树,动态规划) 题面 BZOJ Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总 ...

  7. [BZOJ2286][SDOI2011]消耗战(虚树DP)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4998  Solved: 1867[Submit][Statu ...

  8. 【BZOJ2286】[Sdoi2011]消耗战 虚树

    [BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的 ...

  9. 虚树+【BZOJ2286】【SDOI2011】消耗战(虚树)(DP)

    先看一道题: [BZOJ2286][SDOI2011]消耗战 Description 在一场战争中,战场由n个岛屿和n−1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总 ...

  10. [BZOJ2286][Sdoi2011]消耗战(虚树上DP)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6457  Solved: 2533[Submit][Statu ...

随机推荐

  1. PL/SQL Block Structure

    [顶]ORACLE PL/SQL编程详解之二: PL/SQL块结构和组成元素(为山九仞,岂一日之功) 继上四篇:ORACLE PL/SQL编程之八:把触发器说透                ORAC ...

  2. 转:用WCAT进行IIS压力测试

    Microsoft的Web容量分析工具(WCAT) 是测试你的客户-服务器网络配置的必备工具.这个工具在你的网络上对多种工作量的场景进行仿真,允许你确定你的网络和服务器的最佳配置.WCAT是专门为 评 ...

  3. Framework/base 下添加自定义模块的步骤

    在Android源码编译成功的基础上,重新编译带自己API的android.jar需要进行以下几个步骤操作:1.添加自己的源代码,在android源码的frameworks/base目录下新建一个文件 ...

  4. web—第四章css&第五章

     web—第四章css&第五章 终于迎接等待已久的CSS,在没学这个之前,我们只会用一点img,查一点小图片,或者是用style改一下颜色,而且比较麻烦.现在多了个css在文件夹在创建一个cs ...

  5. jboss的时区问题

    默认情况下,jboss启动时,使用的时区是“+0:00”区,而中国所在的时间为"+8:00"区(所谓的东8区),最终java取当前时间时,总比北京时间慢8个小时 解决办法: 新建一 ...

  6. tkinter 的两个例子

    第一个例子:after 用于定时操作 import tkinter as tk import time class MyApp(tk.Frame): def __init__(self, msecs= ...

  7. Spire.Doc组件读取与写入Word

    之前写了一篇开源组件DocX读写word的文章,当时时间比较匆忙选了这个组件,使用过程中还是有些不便,不能提前定义好模版,插入Form表单域进行替换.最近无意中发现Spire.Doc组件功能很强大,目 ...

  8. typedef 和define的区别

    总结一下typedef和#define的区别 1.概念 #define 它在编译预处理时进行简单的替换,不作正确性检查.它是预处理指令. typedef 它在自己的作用域内给一个已经存在的类型一个别名 ...

  9. 读懂IL代码就这么简单 (一)

    一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉 ...

  10. Bootstrap系列 -- 5. 文本对齐方式

    一. 文本对齐样式 .text-left:左对齐 .text-center:居中对齐 .text-right:右对齐 .text-justify:两端对齐 二. 使用方式 <p class=&q ...