BZOJ2286:[SDOI2011]消耗战——题解
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++
http://www.lydsy.com/JudgeOnline/problem.php?id=2286
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 6Sample Output
12
32
22
UPD 18/5/3:我终于懂虚树啦!以及美化代码。
显然是虚树啊……然而我还是不懂虚树怎么建的……
但是dp十分的简单,就是在虚树上dp[i]表示从i以下的资源节点都到不了i的最小费用。
设u为v的儿子,则:
当u有资源的时候,显然是从v到u的路径中挑一条最小费用的路径割掉即可。
当u没有资源的时候,要么将u往下的资源节点和u断开联系(即将dp[v]+=dp[u]),要么选择从v到u的路径中挑一条最小费用的路径割掉。
#include<algorithm> #include<iostream> #include<cstring> #include<cctype> #include<cstdio> #include<cmath> #include<stack> using namespace std; typedef long long ll; const int N=250005,B=18; const int INF=0x7fffffff; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int w,to,nxt; }e[2*N]; int n,m,cnt,head[N],dep[N]; int anc[N][B+4],val[N][B+4]; int aux[N],pos[N],stk[N],tot; int fa_aux[N],tmp[N],num,top; bool vis[N]; ll dp[N]; inline bool cmp(int a,int b){return pos[a]<pos[b];} inline void add(int u,int v,int w){ e[++cnt].to=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt; } void dfs(int u){ pos[u]=++tot;dep[u]=dep[anc[u][0]]+1; for(int i=1;i<=B;i++){ anc[u][i]=anc[anc[u][i-1]][i-1]; val[u][i]=min(val[u][i-1],val[anc[u][i-1]][i-1]); } for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to,w=e[i].w; if(anc[u][0]!=v){ anc[v][0]=u;val[v][0]=w; dfs(v); } } } inline int LCA(int i,int j){ if(dep[i]<dep[j])swap(i,j); for(int k=B;k>=0;--k) if(dep[anc[i][k]]>=dep[j])i=anc[i][k]; if(i==j)return i; for(int k=B;k>=0;--k) if(anc[i][k]!=anc[j][k]) i=anc[i][k],j=anc[j][k]; return anc[i][0]; } void build(int t){ sort(aux+1,aux+t+1,cmp); num=t;stk[top=0]=0; for(int i=1;i<=t;i++){ int u=aux[i]; if(!top)fa_aux[u]=0,stk[++top]=u; else{ int lca=LCA(u,stk[top]); while(dep[stk[top]]>dep[lca]){ if(dep[stk[top-1]]<=dep[lca]) fa_aux[stk[top]]=lca; top--; } if(stk[top]!=lca){ fa_aux[lca]=stk[top]; stk[++top]=lca; aux[++num]=lca; } fa_aux[u]=lca; stk[++top]=u; } } sort(aux+1,aux+num+1,cmp); } inline ll cut(int u,int v){ int res=INF; for(int i=B;i>=0;i--) if(dep[anc[u][i]]>=dep[v]){ res=min(res,val[u][i]); u=anc[u][i]; } return res; } void solve(){ for(int i=1;i<=num;i++)dp[aux[i]]=0; for(int i=num;i>=1;i--){ int u=aux[i],v=fa_aux[u]; if(!vis[u]) dp[v]+=min(dp[u],cut(u,v)); else dp[v]+=cut(u,v); } } int main(){ n=read(); for(int i=1;i<n;i++){ int u=read(),v=read(),w=read(); add(u,v,w);add(v,u,w); } dfs(1); m=read(); while(m--){ int t=read(); for(int i=1;i<=t;i++){ int u=read(); aux[i]=tmp[i]=u;vis[u]=1; } aux[++t]=1; build(t);solve(); printf("%lld\n",dp[1]); for(int i=1;i<=t;i++)vis[tmp[i]]=0; } }