消耗战题解

消耗战:

[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 }

 

posted @ 2019-05-29 21:05  lsoi_ljk123  阅读(203)  评论(0编辑  收藏  举报