1. 程式人生 > >【BZOJ2286】[Sdoi2011]消耗戰 虛樹

【BZOJ2286】[Sdoi2011]消耗戰 虛樹

algorithm for clas ive 同時 bool 產生 任務 消耗戰

【BZOJ2286】[Sdoi2011]消耗戰

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

題解:特地學了一下虛樹的造法。

我們將每次給出的所有點,以及他們影響到的所有點(即他們中任意兩點的LCA)都拿出來,然後跑個樹形DP就行了。這些點形成的東西叫虛樹,問題是怎麽建呢?

本人naive的做法:將給出的點按dfs序排序,然後求出相鄰兩點的LCA,顯然這些LCA就是所有點對的LCA,所以這說明虛樹的大小是O(k)的。然後我們再將這些點按dfs序排序,然後從左到右模擬DFS的過程,如果下一個點再當前點的子樹中,則遞歸下去,否則回溯。

好吧下面說更NB的做法:先按dfs序排序,然後用棧維護當前的一條鏈(銘記維護的是鏈!),那麽新加入一個點i+1時,先求出i和i+1的lca,找到這個lca在鏈上的位置,然後將lca下面的鏈上的點都彈棧(彈棧的時候順便連邊),最後將lca和i+1扔到棧中。具體做法可以看代碼,當然最好還是畫畫圖理解一下。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=250010;
typedef long long ll;
int n,m,K,cnt,top;
int to[maxn<<1],next[maxn<<1],val[maxn<<1],head[maxn],fa[20][maxn],Log[maxn],dep[maxn];
int p1[maxn],p2[maxn],vis[maxn],p[maxn],st[maxn];
ll s[maxn];
vector<int> ch[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
inline int MN(int a,int b)	{return dep[a]<dep[b]?a:b;}
void dfs(int x)
{
	p1[x]=++p2[0];
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[0][x])
		dep[to[i]]=dep[x]+1,fa[0][to[i]]=x,s[to[i]]=min(s[x],(ll)val[i]),dfs(to[i]);
	p2[x]=p2[0];
}
inline int lca(int a,int b)
{
	if(dep[a]<dep[b])	swap(a,b);
	for(int i=Log[dep[a]-dep[b]];i>=0;i--)	if(dep[fa[i][a]]>=dep[b])	a=fa[i][a];
	if(a==b)	return a;
	for(int i=Log[dep[a]];i>=0;i--)	if(fa[i][a]!=fa[i][b])	a=fa[i][a],b=fa[i][b];
	return fa[0][a];
}
bool cmp(int a,int b)
{
	return p1[a]<p1[b];
}
inline void Add(int a,int b)
{
	if(b)	ch[a].push_back(b);
}
ll solve(int x)
{
	ll tmp=0;
	for(int i=0;i<(int)ch[x].size();i++)	tmp+=solve(ch[x][i]);
	ch[x].clear();
	if(!vis[x])	tmp=min(tmp,s[x]);
	else	tmp=s[x];
	return tmp;
}
int main()
{
	n=rd();
	int i,j,a,b,c;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
	dep[1]=1,s[1]=1ll<<60,dfs(1);
	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
	for(j=1;(1<<j)<=n;j++)	for(i=1;i<=n;i++)	fa[j][i]=fa[j-1][fa[j-1][i]];
	m=rd();
	for(i=1;i<=m;i++)
	{
		K=rd();
		for(j=1;j<=K;j++)	p[j]=rd(),vis[p[j]]=1;
		sort(p+1,p+K+1,cmp);
		st[top=1]=p[1];
		for(j=2;j<=K;j++)
		{
			a=p[j],b=lca(st[top],a),c=0;
			while(top&&dep[st[top]]>dep[b])	Add(st[top],c),c=st[top--];
			if(st[top]==b)	Add(st[top],c);
			if(dep[st[top]]<dep[b])	Add(b,c),st[++top]=b;
			st[++top]=a;
		}
		while(top>1)	Add(st[top-1],st[top]),top--;
		a=st[1];
		printf("%lld\n",solve(a));
		for(j=1;j<=K;j++)	vis[p[j]]=0;
	}
	return 0;
}//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

【BZOJ2286】[Sdoi2011]消耗戰 虛樹