zoukankan      html  css  js  c++  java
  • BZOJ 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 6

    Sample Output


    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)
    对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1


    HINT

     

    Source

     首先很明显是树形dp,开始我yy了一个暴力dp,f[i][0]表示以i为根的子树完全与能源点隔断的最小代价,f[i][1]表示以i为根的子树没有完全与能源点隔断的最小代价。但后来发现其实完全没有必要这么做。
    令best[i]表示从i到根的边权的最小值,f[i]表示切断i的子树的最小代价。转移:f[i]=min(best[i],Σf[son])。
    但是裸的dp的话肯定是会TLE的。我们仔细想想,每次询问都有许多冗余的点来浪费复杂度,所以我们可以只用询问点及他们的LCA来建一颗新树,我们暂且称其为虚树,然后在虚树上跑dp,效率就会高很多(当然,去除了很多没有用的点嘛)。
    那么问题就来了,怎么建虚树呢???我们利用的是单调栈。
    将所有询问点按照dfs序排一遍序,然后,一次加入栈中(栈中初始化为节点1)。若枚举的节点h[i]与栈顶元素的s[top] 的LCA fa的深度比栈顶元素深度小,s[top-1]向s[top]连接一条边,栈顶再见,fa与h[i]都加入栈中。就这样,一颗虚树建立出来了。(具体实现见代码)
     
      1 #include<cstring>
      2 #include<iostream>
      3 #include<cstdio>
      4 #include<algorithm>
      5 #include<cstdlib>
      6 using namespace std;
      7 
      8 typedef long long ll;
      9 #define maxn (250010)
     10 #define inf (1LL<<60)
     11 int n,m,cnt,side[maxn],next[maxn*2],dep[maxn],h[maxn],st[maxn],sign[maxn];
     12 int toit[maxn*2],cost[maxn*2],f[maxn][30],id[maxn],ID;
     13 ll g[maxn],best[maxn];
     14 
     15 inline int read()
     16 {
     17     int x=0,f=1;char ch=getchar();
     18     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     19     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
     20     return x*f;
     21 }
     22 
     23 inline bool cmp(int a,int b) { return id[a] < id[b]; }
     24 
     25 inline void add(int a,int b,int c)
     26 {
     27     next[++cnt] = side[a]; side[a] = cnt;
     28     toit[cnt] = b; cost[cnt] = c;
     29 }
     30 
     31 inline void ins(int a,int b,int d)
     32 {
     33     if (a == b) return;
     34     if (sign[a] != d) side[a] = 0,sign[a] = d;
     35     if (sign[b] != d) side[b] = 0,sign[b] = d;
     36     next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b;
     37 }
     38 
     39 inline void dfs(int now)
     40 {
     41     id[now] = ++ID; 
     42     for (int i = 1;1 << i <= dep[now];++i)
     43         f[now][i] = f[f[now][i-1]][i-1];
     44     for (int i = side[now];i;i = next[i])
     45         if (toit[i] != f[now][0])
     46         {
     47             best[toit[i]] = min(best[now],(ll)cost[i]);
     48             dep[toit[i]] = dep[now] + 1;
     49             f[toit[i]][0] = now;
     50             dfs(toit[i]);
     51         }
     52 }
     53 
     54 inline void jump(int &a,int step)
     55 {
     56     int i = 0;
     57     for (;step;step >>= 1,++i) if (step&1) a = f[a][i];
     58 }
     59 
     60 inline int lca(int a,int b)
     61 {
     62     if (dep[a]<dep[b]) swap(a,b);
     63     jump(a,dep[a]-dep[b]);
     64     if (a == b) return a;
     65     for (int i = 0;i >= 0;)
     66     {
     67         if (f[a][i] != f[b][i]) a = f[a][i],b = f[b][i],++i;
     68         else --i;
     69     }
     70     return f[a][0];
     71 }
     72 
     73 inline void dp(int now)
     74 {
     75     g[now] = best[now];
     76     ll tmp = 0;
     77     for (int i = side[now];i;i = next[i])
     78         dp(toit[i]),tmp += g[toit[i]];
     79     if (tmp && tmp <= g[now]) g[now] = tmp;
     80 }
     81 
     82 inline void work(int p)
     83 {
     84     cnt = 0;
     85     int K = read(),tot,top;
     86     for (int i = 1;i <= K;++i) h[i] = read();
     87     sort(h+1,h+K+1,cmp);
     88     h[tot = 1] = h[1];
     89     for (int i = 2;i <= K;++i) if (lca(h[tot],h[i]) != h[tot]) h[++tot] = h[i];
     90     st[top = 1] = 1;
     91     for (int i = 1;i <= tot;++i)
     92     {
     93         int ans = lca(h[i],st[top]);
     94         while (true)
     95         {
     96             if (dep[ans] >= dep[st[top-1]])
     97             {
     98                 ins(ans,st[top--],p);
     99                 break;
    100             }
    101             ins(st[top-1],st[top],p); --top;
    102         }
    103         if (st[top] != ans) st[++top] = ans;
    104         if (st[top] != h[i]) st[++top] = h[i];
    105     }
    106     while (--top) ins(st[top],st[top+1],p);
    107     dp(1);
    108     printf("%lld
    ",g[1]);
    109 }
    110 
    111 int main()
    112 {
    113     freopen("2286.in","r",stdin);
    114     freopen("2286.out","w",stdout);
    115     scanf("%d",&n);
    116     for (int i = 1;i < n;++i)
    117     {
    118         int a = read(),b = read(),c = read();
    119         add(a,b,c); add(b,a,c);
    120     }
    121     dep[0] = -1; best[1] = inf; dfs(1);
    122     scanf("%d",&m);
    123     for (int i = 1;i <= m;++i) work(i);    
    124     fclose(stdin); fclose(stdout);
    125     return 0;
    126 }
    View Code

    如果觉得我的代码和hzwer的很像,你就假设长得不像吧。

  • 相关阅读:
    查看mysql版本的四种方法及常用命令
    newInstance和new的区别(good)
    Citrix 服务器虚拟化之六 Xenserver虚拟机创建与快照
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 前缀表达式
    Java实现 蓝桥杯 算法训练 前缀表达式
  • 原文地址:https://www.cnblogs.com/mmlz/p/4278949.html
Copyright © 2011-2022 走看看