bzoj 2286: [Sdoi2011]消耗战
Description
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
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
10
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
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
12
32
22
32
22
HINT
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
题解:
这题树形DP非常好想
定义f[i]为i的子树内全部摧毁的最小代价。
f[i]=min(sigma(f[u]),e) u为i的所有儿子,e为父亲边权值
那么这样复杂度是O(nm)的
所以要构建虚树:
虚树的作用:在对答案无影响的情况下,将无关节点去处,只保留相关节点
构建思路:大致是用栈维护一条时间戳递增的链,具体方法参见其他博客
然后在上述DP上稍加修改:把e改为(i到根路径上代价最小的即可) 可以预处理出来。
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #define RG register 8 using namespace std; 9 typedef long long ll; 10 const int N=250015,inf=2e8; 11 int n,head[N],num=1,lim,DFN=0; 12 struct Lin{ 13 int next,to,dis; 14 }a[N<<1],e[N<<1]; 15 void addedge(int x,int y,int z){ 16 a[++num].next=head[x]; 17 a[num].to=y; 18 a[num].dis=z; 19 head[x]=num; 20 } 21 int q[N],dfn[N],dep[N],val[N],fa[N][19]; 22 int Lca(int x,int y){ 23 if(dep[x]<dep[y])swap(x,y); 24 int deep=dep[x]-dep[y]; 25 for(int i=lim;i>=0;i--) 26 if((1<<i)&deep)x=fa[x][i]; 27 if(x==y)return x; 28 for(int i=lim;i>=0;i--){ 29 if(fa[x][i]!=fa[y][i]) 30 x=fa[x][i],y=fa[y][i]; 31 } 32 return fa[x][0]; 33 } 34 bool comp(int i,int j){ 35 return dfn[i]<dfn[j]; 36 } 37 int st[N],H[N],NUM=0;ll f[N]; 38 void init(int x,int y){ 39 if(x==y)return ; 40 e[++NUM].next=H[x];e[NUM].to=y;H[x]=NUM; 41 e[++NUM].next=H[y];e[NUM].to=x;H[y]=NUM; 42 } 43 void dfs(int x,int last){ 44 RG int u;ll tot=0; 45 if(x!=1)f[x]=val[x]; 46 else f[x]=2e18; 47 for(RG int i=H[x];i;i=e[i].next){ 48 u=e[i].to;if(u==last)continue; 49 dfs(u,x);tot+=f[u]; 50 } 51 H[x]=0; 52 if(tot && tot<f[x])f[x]=tot; 53 } 54 void solve(){ 55 NUM=0; 56 int m;scanf("%d",&m); 57 for(RG int i=1;i<=m;i++)scanf("%d",&q[i]); 58 sort(q+1,q+m+1,comp); 59 int s=1; 60 for(RG int i=2;i<=m;i++)if(Lca(q[i],q[s])!=q[s])q[++s]=q[i]; 61 int top=0,lca; 62 st[++top]=1; 63 for(RG int i=1;i<=s;i++){ 64 lca=Lca(q[i],st[top]); 65 while(1){ 66 if(dep[st[top-1]]<=dep[lca]){ 67 init(st[top],lca);top--; 68 if(st[top]!=lca)st[++top]=lca; 69 break; 70 } 71 init(st[top],st[top-1]);top--; 72 } 73 st[++top]=q[i]; 74 } 75 while(top>1)init(st[top-1],st[top]),top--; 76 dfs(1,1); 77 printf("%lld\n",f[1]); 78 } 79 void prework(int x){ 80 RG int u; 81 dfn[x]=++DFN; 82 for(RG int i=head[x];i;i=a[i].next){ 83 u=a[i].to;if(dep[u])continue; 84 val[u]=min(val[x],a[i].dis); 85 dep[u]=dep[x]+1; 86 fa[u][0]=x; 87 prework(u); 88 } 89 } 90 void work() 91 { 92 RG int x,y,z; 93 scanf("%d",&n);lim=log(n)/log(2)+1; 94 for(int i=1;i<n;i++){ 95 scanf("%d%d%d",&x,&y,&z); 96 addedge(x,y,z);addedge(y,x,z); 97 } 98 dep[1]=1;val[1]=inf;prework(1); 99 for(RG int j=1;j<=lim;j++) 100 for(RG int i=1;i<=n;i++) 101 fa[i][j]=fa[fa[i][j-1]][j-1]; 102 int Q; 103 scanf("%d",&Q); 104 while(Q--) 105 solve(); 106 } 107 108 int main() 109 { 110 work(); 111 return 0; 112 }