BZOJ2286: [Sdoi2011]消耗战
2286: [Sdoi2011]消耗战
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 4833 Solved: 1788
[Submit][Status][Discuss]
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
HINT
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
题解
\(dp[i]\)表示以\(i\)为根的子树,使黑点不与\(i\)连通的最小割边权值和。
转移有
其中\(mi[i]\)表示点i到根的路径上的最小边权
题目有\(\sum_{i=1}^{k}k_i \leq 10^5\)
所以用虚树即可,可以证明点数是\(n\log_n\)级别的
注意不要每次都清空图,否则复杂度退化为nm会T的
记录下那些head需要被清0,同时不要全部开longlong,longlong真心慢,会T
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <map>
#include <cmath>
inline long long max(long long a, long long b){return a > b ? a : b;}
inline long long min(long long a, long long b){return a < b ? a : b;}
inline long long abs(long long x){return x < 0 ? -x : x;}
inline void swap(int &x, int &y){long long tmp = x;x = y;y = tmp;}
template <class T>
inline void read(T &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
}
const long long INF = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 250000 + 10;
//Tree
struct Edge
{
int u,v,nxt;
long long w;
Edge(int _u, int _v, long long _w, int _nxt){u = _u, v = _v, w = _w, nxt = _nxt;}
Edge(){}
}edge1[MAXN << 1], edge2[MAXN << 1];
int head1[MAXN << 1], head2[MAXN << 1], cnt1, cnt2, deep[MAXN], p[30][MAXN], dfn[MAXN], tt, M, n, m;
long long mi[MAXN];
int node[MAXN], tot, stack[MAXN * 20], top, tag[MAXN], re[MAXN], pt;
inline void insert1(int a, int b, long long c)
{
edge1[++ cnt1] = Edge(a, b, c, head1[a]), head1[a] = cnt1;
edge1[++ cnt1] = Edge(b, a, c, head1[b]), head1[b] = cnt1;
}
inline void insert2(int a, int b, long long c = 0)
{
edge2[++ cnt2] = Edge(a, b, c, head2[a]), head2[a] = cnt2;
re[++ pt] = a;
}
void dfs(int x)
{
dfn[x] = ++ tt;
for(int pos = head1[x];pos;pos = edge1[pos].nxt)
{
int v = edge1[pos].v;
if(v == p[0][x]) continue;
deep[v] = deep[x] + 1, mi[v] = min(mi[x], edge1[pos].w), p[0][v] = x;
dfs(v);
}
}
void yuchuli()
{
while((1 << M) <= n) ++ M;-- M;
for(int i = 1;i <= M;++ i)
for(int j = 1;j <= n;++ j)
p[i][j] = p[i - 1][p[i - 1][j]];
}
int LCA(int va, int vb)
{
if(deep[va] < deep[vb]) swap(va, vb);
for(int i = M;i >= 0;-- i)
if(deep[va] - deep[vb] >= (1 << i))
va = p[i][va];
if(va == vb) return va;
for(int i = M;i >= 0;-- i)
if(p[i][va] != p[i][vb])
va = p[i][va], vb = p[i][vb];
return p[0][va];
}
//Virtual Tree
bool cmp(int a, int b)
{
return dfn[a] < dfn[b];
}
void build_VT()
{
cnt2 = pt = top = 0;
std::sort(node + 1, node + 1 + tot, cmp);
for(int i = 1;i <= tot;++ i)
{
if(top == 0)
{
stack[++ top] = node[i];
continue;
}
int lca = LCA(stack[top], node[i]);
while(deep[lca] < deep[stack[top]])
{
if(deep[lca] >= deep[stack[top - 1]])
{
insert2(lca, stack[top]);
if(stack[-- top] != lca) stack[++ top] = lca;
break;
}
insert2(stack[top - 1], stack[top]), -- top;
}
stack[++ top] = node[i];
}
while(top > 1) insert2(stack[top - 1], stack[top]), -- top;
}
//DP
long long dp[MAXN];
void DP(int x, int pre)
{
long long sum = 0;
for(int pos = head2[x];pos;pos = edge2[pos].nxt)
{
int v = edge2[pos].v;
DP(v, x);
sum += dp[v];
}
if(tag[x] || !sum) dp[x] = mi[x];
else dp[x] = min(mi[x], sum);
}
int main()
{
memset(mi, 0x3f, sizeof(mi)), memset(dp, 0x3f, sizeof(dp));
read(n);
for(int i = 1;i < n;++ i)
{
int tmp1, tmp2;
long long tmp3;
read(tmp1), read(tmp2), read(tmp3);
insert1(tmp1, tmp2, tmp3);
}
dfs(1);yuchuli();
read(m);
for(int i = 1;i <= m;++ i)
{
read(tot);
for(int j = 1;j <= tot;++ j)
read(node[j]), tag[node[j]] = 1;
build_VT(), DP(stack[1], -1);
for(int j = 1;j <= tot;++ j)
tag[node[j]] = 0;
for(int i = 1;i <= pt;++ i)
head2[re[i]] = 0;
printf("%lld\n", dp[stack[1]]);
}
return 0;
}