BZOJ2286 [Sdoi2011]消耗战
Description
Input
第一行一个整数n,代表岛屿数量。
接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。
第n+1行,一个整数m,代表敌方机器能使用的次数。
接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。
Output
输出有m行,分别代表每次任务的最小代价。
Sample Input
1 5 13
1 9 6
2 1 19
2 4 8
2 3 91
5 6 8
7 5 4
7 8 31
10 7 9
3
2 10 6
4 5 7 8 3
3 9 4 6
Sample Output
32
22
HINT
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
正解:虚树+树形DP
解题报告:
听说这是虚树裸题,于是我跑过来AC了。
考虑直接树形DP,点数过多,而且大多数没有用,会超时。那我们想保留一些必要的点,而去掉无用的点。
首先虚树的构建也是一个很神的算法。网上代码很多,但是总结还是少。
遥遥的题解,传送门:http://user.qzone.qq.com/872191552/blog/1464431226
考虑维护一个栈,每次往里面插新结点,然后特判一下二者的lca和栈顶元素的情况。具体的看遥遥的吧,懒得说了,等切世界树的时候再写一份详细的吧。
UPD:
昨天太懒了。现在还是仔细说一遍吧。
具体做法挺简单,就是要想清楚才行。首先把询问点根据原树DFS序排序,显然这些点都要出现在虚树中来,而且为了保证结构不被破坏,另外一些跟他们有关系的点都要加入到虚树中来。我们用一个栈,维护原树上的一条链,自栈底到栈顶,深度由小变大。每次考虑插入询问点进栈。如果插入点的祖先是栈顶元素,那么直接插入即可,因为反正是一条链上的结点。如果不是的话,那么只有可能分居他们的lca的两棵子树中。现在我们就需要分类讨论,如果栈顶元素的下一位的深度比lca深,那么我们需要不断弹出栈顶元素,并且在弹出之前与栈顶下一位连一下边。直到lca深度比栈顶元素深,此时把lca加入栈,把需要插入的点加入栈,继续往下处理。又因为我们是按DFS序做的,这样就可以保证我们开始说的,维护的是树上的一条链。之后再DP就可以了。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXN = 250011; 21 LL inf;//inf不能开小了 22 int n,m,ecnt,tot,id[MAXN],k; 23 int first[MAXN],next[MAXN*2],to[MAXN*2],w[MAXN*2]; 24 LL val[MAXN];//这个点到根上的最小边权 25 int jump[MAXN][19],deep[MAXN]; 26 int que[MAXN],top,Stack[MAXN]; 27 int head[MAXN]; 28 LL f[MAXN]; 29 30 inline int getint() 31 { 32 int w=0,q=0; 33 char c=getchar(); 34 while((c<'0' || c>'9') && c!='-') c=getchar(); 35 if (c=='-') q=1, c=getchar(); 36 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 37 return q ? -w : w; 38 } 39 40 struct edge{ 41 int to,next; 42 }e[MAXN]; 43 44 inline LL min(LL x,LL y){ if(x<y) return x; return y; } 45 46 inline void dfs(int x,int fa){ 47 jump[x][0]=fa; id[x]=++ecnt;//作出dfs序 48 for(int i=1;i<=18;i++) jump[x][i]=jump[jump[x][i-1]][i-1]; 49 for(int i=first[x];i;i=next[i]) { 50 int v=to[i]; 51 if(v==fa) continue; 52 val[v]=min(w[i],val[x]); deep[v]=deep[x]+1; 53 dfs(v,x); 54 } 55 } 56 57 inline bool cmp(int a,int b){return id[a]<id[b];} 58 59 inline int lca(int x,int y){ 60 if(deep[x]<deep[y]) swap(x,y); 61 int t=0; while((1<<t) <= deep[x]) t++; 62 t--; for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=jump[x][i]; 63 if(x==y) return y; 64 for(int i=t;i>=0;i--) if(jump[x][i]!=jump[y][i]) { x=jump[x][i]; y=jump[y][i]; } 65 return jump[x][0]; 66 } 67 68 inline void link(int x,int y){ if(x==y) return ; e[++ecnt].next=head[x]; head[x]=ecnt; e[ecnt].to=y;} 69 70 inline void dp(int x){ 71 LL lin=0; f[x]=val[x]; 72 for(int i=head[x];i;i=e[i].next) { 73 dp(e[i].to); 74 lin+=f[e[i].to]; 75 } 76 head[x]=0;//退出的时候顺便清空 77 if(!lin) f[x]=val[x]; 78 else if(lin<f[x]) f[x]=lin; 79 } 80 81 inline void solve(){//断绝到根结点1的路径 82 m=getint(); for(int i=1;i<=m;i++) que[i]=getint(); 83 sort(que+1,que+m+1,cmp);//按dfs序排序 84 tot=0; que[++tot]=que[1]; 85 for(int i=2;i<=m;i++) if(lca(que[i],que[tot])!=que[tot]) que[++tot]=que[i];//应该是和tot比较 86 //在下面的肯定不用计算,只要切断上部的即可 87 top=0;Stack[++top]=1; int grand;//最近公共祖先 88 ecnt=0; 89 for(int i=1;i<=tot;i++) {//分类讨论 90 grand=lca(Stack[top],que[i]); 91 while(1) { 92 if(deep[Stack[top-1]]<=deep[grand]) {//分别处在两个子树,grand深度更大!!! 93 link(grand,Stack[top]); top--; 94 if(Stack[top]!=grand) Stack[++top]=grand; 95 break; 96 } 97 link(Stack[top-1],Stack[top]); top--; 98 } 99 if(Stack[top]!=que[i]) Stack[++top]=que[i];//在同一子树 100 } 101 top--; 102 while(top) link(Stack[top],Stack[top+1]),top--;//剩余的记得连上 103 dp(1); 104 printf(OT"\n",f[1]); 105 } 106 107 inline void work(){ 108 n=getint(); int x,y,z; 109 inf=1;for(int i=1;i<=60;i++) inf*=2; 110 for(int i=1;i<n;i++) { 111 x=getint(); y=getint(); z=getint(); 112 next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; 113 next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z; 114 } 115 val[1]=inf; ecnt=0; deep[1]=0; dfs(1,0); 116 k=getint(); for(int i=1;i<=k;i++) solve(); 117 } 118 119 int main() 120 { 121 work(); 122 return 0; 123 }