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的很像,你就假设长得不像吧。

  • 相关阅读:
    .NET面试题系列[2]
    .NET面试题系列[1]
    被淡忘的c#析构函数
    关于Spring IOC容器解释
    工作随笔记 点击除div自身之外的地方,关闭自己
    js获得控件位置
    PHP如何判断对象为空的方法分享
    PHP 网页调用本地exe程序实例
    PHP jQuery实现上传图片时预览图片的功能实例
    Yii 自带的分页实例
  • 原文地址:https://www.cnblogs.com/mmlz/p/4278949.html
Copyright © 2011-2022 走看看