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)连通的最小割边权值和。
转移有

[i is black : dp[i] = mi[i] ]

[i is white : dp[i] = min(mi[i], sum dp[son_i]) ]

其中(mi[i])表示点i到根的路径上的最小边权

题目有(sum_{i=1}^{k}k_i leq 10^5)
所以用虚树即可,可以证明点数是(nlog_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
", dp[stack[1]]);	
	}
    return 0;
}
原文地址:https://www.cnblogs.com/huibixiaoxing/p/8543703.html