消耗战题解
消耗战:
[SDOI2011] 消耗战
题目描述
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
输入
第一行一个整数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,表示资源丰富岛屿的编号。
输出
输出有m行,分别代表每次任务的最小代价。
样例输入
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
样例输出
12
32
22
提示
对于10%的数据,2<=n<=10,1<=m<=5,1<=ki<=n-1
对于20%的数据,2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)
对于40%的数据,2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)
题意:
n个点,n-1条边的树,给出断n-1条边的代价。
m次询问,每次给出一定数量的点,求满足它们不能与1连通的最小代价。
思路:
考虑树型Dp
对于每个点,我们可以选择断掉它与点1路径上权值最小的边,或者它的儿子们按此规则不与1连通。
所以取最小值返回给父亲。
为了方便,将“与点1路径上权值最小的边”称为“最小路径”。
为了求“最小路径”,我们可以用dfs来预处理。
当然,这样会有问题:
不能与1连通的点中如果有两点在同一条链上,且深度高的点的“最小路径”在两点间的路径上:
按上述规则,会返回深度高的点的“最小路径”的权值;
但是我们必须断深度低的点,应返回深度低的点的“最小路径”的权值;
其实也就是说深度高的点的“最小路径”的权值对答案无贡献,
在不能与1连通的点中,我们只需要记录一条链上深度最低的点。
又因有多次询问,直接DP会超时。
仔细观察题目,会发现m次询问中给出的不能与1连通的点的数量的和不超过//看题
想到虚树。
代码:
1 #include<bits/stdc++.h> 2 #define re register 3 using namespace std; 4 const int N=250006,M=500006; 5 int n,m,t1,t2,t3,head[N],cnt=0,minw[N],d[N],ff[N][21],dfn[N],top,x[N],s[N]; 6 struct edge{int nxt,to,w;}e[N<<1]; 7 vector<int> f[M]; 8 inline void add(int u,int v,int w){e[++cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt;} 9 inline int read() 10 { 11 int T=0,F=1; char ch=getchar(); 12 while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} 13 while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); 14 return F*T; 15 } 16 void dfs(int u,int fa) 17 { 18 d[u]=d[fa]+1; ff[u][0]=fa; dfn[u]=++cnt; 19 for(int i=1;i<=17;++i) ff[u][i]=ff[ff[u][i-1]][i-1]; 20 for(int i=head[u];i;i=e[i].nxt) 21 if(e[i].to!=fa) minw[e[i].to]=min(e[i].w,minw[u]),dfs(e[i].to,u); 22 } 23 inline int lca(int u,int v) 24 { 25 if(d[u]<d[v]) swap(u,v); 26 for(int i=17;i>=0;--i) if(d[u]>=d[v]+(1<<i)) u=ff[u][i]; 27 if(u==v) return u; 28 for(int i=17;i>=0;--i) 29 { 30 if(ff[u][i]==ff[v][i]) continue; 31 u=ff[u][i],v=ff[v][i]; 32 } 33 return ff[u][0]; 34 } 35 void ins(int u) 36 { 37 if(top==1){s[++top]=u; return;} 38 int t=lca(u,s[top]); 39 if(s[top]==t) return; 40 while(top>1&&d[s[top-1]]>=d[t]) f[s[top-1]].push_back(s[top]),--top; 41 if(t!=s[top]) f[t].push_back(s[top]),s[top]=t; 42 if(t!=u) s[++top]=u; 43 } 44 long long dp(int u) 45 { 46 long long sum=0,p=minw[u]; 47 if(!f[u].size()) return p; 48 for(int i=0;i<f[u].size();++i) sum+=dp(f[u][i]); 49 f[u].clear(); if(u==1) return sum; 50 return min(sum,p); 51 } 52 bool cmp(int u,int v){return dfn[u]<dfn[v];} 53 int main() 54 { 55 n=read(); 56 for(re int i=1;i<n;++i) t1=read(),t2=read(),t3=read(),add(t1,t2,t3),add(t2,t1,t3); 57 cnt=0; minw[1]=1e7; dfs(1,0); m=read(); 58 for(re int i=1;i<=m;++i) 59 { 60 t1=read(); top=1; 61 for(int j=1;j<=t1;++j) x[j]=read(); 62 sort(x+1,x+t1+1,cmp); s[top]=1; 63 for(int j=1;j<=t1;++j) ins(x[j]); 64 while(top>1) f[s[top-1]].push_back(s[top]),--top; 65 printf("%lld\n",dp(1)); 66 } 67 return 0; 68 }