P2495 [SDOI2011] 消耗戰

liuboom發表於2024-12-06

P2495 [SDOI2011] 消耗戰

題意簡述:

在一顆樹上,每次給定\(k\)個關鍵點,求切斷1到這些點的最小花費的總和(最小化總花費)

我們考慮dp:記 \(dp[x]\)為切斷\(dp[x]\)內的所有點到1的路徑的最小花費
顯然我們有兩種切法,一種是對每個子樹下的\(dp[to]\) 求和,另一種是記錄一個\(mincut[x]\),表示1->x這段路上的所有邊中最小的邊權

但這樣的時間複雜度是O(nq)的,顯然不行

那我們該怎麼辦呢?

虛樹閃亮登場:

虛樹,顧名思義是一顆虛構的樹
它只記錄了關鍵節點,在本題中,注意到

$ \sum k_i \leq 5\times 10^5$

所以我們考慮建立一顆樹,上面只包含關鍵點和他們的lca

虛樹的建立:

參考dalao題解:

Link

只要我們建立了虛樹,然後直接對每個虛樹結點進行樹上dp然後我們這題就做完了

記得每次做完之後清空陣列和之前虛樹建的邊!!!

Code

#include<bits/stdc++.h>
#define int long long
const int N=5e5+5;
const int inf=1e17;
const int lg=20;
using namespace std;
int n,m,e1_cnt,e2_cnt,dfn_cnt,top;
int head1[N],head2[N],mincut[N],dfn[N],que[N],tag[N];
int dep[N],st[N];
int f[N][lg+5];
struct Edge{
	int to,nxt,w;
}e1[N<<1],e2[N<<1];
void add(int x,int y,int w,Edge e[],int head[],int &cnt)
{
	e[++cnt]={y,head[x],w};
	head[x]=cnt;
}
void dfs1(int x)
{
	dfn[x]=++dfn_cnt;
	for(int i=1;i<=lg;i++)
	{
		f[x][i]=f[f[x][i-1]][i-1];
	}
	for(int i=head1[x],to,w;i;i=e1[i].nxt)
	{
		to=e1[i].to,w=e1[i].w;
		if(!dfn[to])
		{
			dep[to]=dep[x]+1;
			mincut[to]=min(mincut[x],w);
			f[to][0]=x;
			dfs1(to);
		}
	}
}
int LCA(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int i=lg;i>=0;i--)
	{
		if(dep[y]<=dep[f[x][i]])x=f[x][i];
	}
	if(x==y)return x;
	for(int i=lg;~i;i--)
	{
		if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	}
	return f[x][0];
}
int dfs2(int x)
{
	int sum=0,res=inf;
	for(int i=head2[x],to;i;i=e2[i].nxt)
	{
		to=e2[i].to;
		sum+=dfs2(to);
	}
	if(tag[x])res=mincut[x];
	else res=min(mincut[x],sum);
	tag[x]=0;
	head2[x]=0;
	return res;
}
bool cmp(int x,int y)
{
	return dfn[x]<dfn[y];
}
void solve()
{
	int k;
	scanf("%lld",&k);
	for(int i=1;i<=k;i++)
	{
		scanf("%lld",&que[i]);
		tag[que[i]]=1;
	}
	sort(que+1,que+1+k,cmp);
	top=1;
	st[top]=que[1];
	for(int i=2;i<=k;i++)
	{
		int now=que[i];
		int lca=LCA(now,st[top]);
		while(1)
		{
			if(dep[lca]>=dep[st[top-1]]) //case1 or case2 or case3
			{
				if(lca!=st[top])//case2 or case 3
				{
					add(lca,st[top],0,e2,head2,e2_cnt);
					if(lca!=st[top-1])//case2
					{
						st[top]=lca;//st.pop(top) and st.push(lca)
					}
					else //case3
					{
						top--;
					}
				}
				// else : case 1
				break;
			}
			else//case 4
			{
				add(st[top-1],st[top],0,e2,head2,e2_cnt);
				top--;
			}
		}
		st[++top]=now;//case 1 2 3 4
	}
	while(--top)
	{
		add(st[top],st[top+1],0,e2,head2,e2_cnt);
	}
	int ans=dfs2(st[1]);
	printf("%lld\n",ans);
	e2_cnt=1;
}
void work()
{
	mincut[1]=inf;
	cin>>n;
	for(int i=1,x,y,w;i<n;i++)
	{
		scanf("%lld%lld%lld",&x,&y,&w);
		add(x,y,w,e1,head1,e1_cnt);
		add(y,x,w,e1,head1,e1_cnt);
	}
	dfs1(1);
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		solve();
	}
	
}
#undef int
int main()
{
	//freopen("P2495.in","r",stdin);//freopen("P2495.out","w",stdout);
	work();
}

相關文章