消耗战

消耗战

在一场战争中,战场由 $n$ 个岛屿和 $n-1$ 个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为 $1$ 的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他 $k$ 个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到 $1$ 号岛屿上)。不过侦查部门还发现了这台机器只能够使用 $m$ 次,所以我们只需要把每次任务完成即可。

 

 对于100%的数据, $2 \le n \le 250000,m \ge 1, \sum{k_i} \le 500000,1 \le k_i \le n-1 $


Solution

每次m个点,我们可以考虑建虚树。

割断一个点的代价为他到虚树上父亲的路径最小值。

然后dp就行。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<vector>
 8 #define ll long long
 9 #define maxn 500005
10 using namespace std;
11 int n,m,num,head[maxn],tot,deep[maxn],f[maxn][22]; 
12 int a[maxn],zh[maxn],top,dy[maxn],dfn[maxn],sc,ins[maxn],cnt,flag[maxn];
13 ll dp[maxn],g[maxn]; 
14 vector<int>G[maxn];
15 struct node{
16     int v,nex;ll w;
17 }e[maxn*2]; 
18 void add(int t1,int t2,ll t3){
19     e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1];head[t1]=tot;
20 }
21 void dfs(int k,int fa,ll Min){
22     dfn[k]=++sc;dy[sc]=k;g[k]=Min;
23     deep[k]=deep[fa]+1;f[k][0]=fa;
24     for(int i=head[k];i;i=e[i].nex){
25         if(e[i].v==fa)continue;
26         dfs(e[i].v,k,min(Min,e[i].w));
27     }
28 }
29 void lj(int t1,int t2){
30     G[t1].push_back(t2);ins[++cnt]=t1;
31 }
32 int Lca(int u,int v){
33     if(deep[u]<deep[v])swap(u,v);
34     for(int x=20;x>=0;x--)if(deep[f[u][x]]>=deep[v])u=f[u][x];
35     for(int x=20;x>=0;x--)if(f[u][x]!=f[v][x])u=f[u][x],v=f[v][x];
36     return u==v?u:f[u][0];
37 }
38 void work(int k){
39     ll sum=0;
40     for(int i=0;i<G[k].size();i++){
41         work(G[k][i]);
42         sum+=dp[G[k][i]];
43     }
44     if(flag[k])dp[k]=g[k],flag[k]=0;
45     else dp[k]=min(g[k],sum);
46 }
47 int main(){
48     cin>>n;
49     for(int i=1,t1,t2;i<n;i++){
50         ll t3;
51         scanf("%d%d%lld",&t1,&t2,&t3);
52         add(t1,t2,t3);add(t2,t1,t3); 
53     }
54     dfs(1,0,1e15);
55     for(int j=1;j<=20;j++)
56     for(int i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];
57     cin>>m;
58     while(m--){
59         scanf("%d",&num);
60         for(int i=1,t;i<=num;i++){
61             scanf("%d",&t);a[i]=dfn[t];flag[t]=1;
62         }
63         sort(a+1,a+num+1);
64         top=1;zh[top]=1;cnt=0;
65         for(int i=1;i<=num;i++){
66             while(1){
67                 int l=Lca(dy[a[i]],zh[top]);
68                 if(l==zh[top])break;
69                 if(dfn[l]<dfn[zh[top-1]]){
70                     lj(zh[top-1],zh[top]);top--;
71                 }
72                 else if(dfn[l]==dfn[zh[top-1]]){
73                     lj(zh[top-1],zh[top]);top--;break;
74                 }
75                 else {
76                     lj(l,zh[top]);zh[top]=l;break;
77                 }
78             }
79             zh[++top]=dy[a[i]];
80         }
81         while(top>1)lj(zh[top-1],zh[top]),top--;
82         work(1);
83         for(int i=1;i<=cnt;i++)G[ins[i]].clear();
84         printf("%lld\n",dp[1]); 
85     }
86     return 0;
87 }
View Code

 

posted @ 2019-04-05 13:16 liankewei123456 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值