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

posted @ 2015-02-07 16:11  lmxyy  阅读(1044)  评论(0编辑  收藏  举报