bzoj2286: [Sdoi2011消耗战

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2286

思路:构建虚树,treeDP,

设f[i]表示i的子树所有资源点断开所需代价,dis[i]表示=i到1的路径上的最小边权

那么如果i是有资源的点,f[i]=dis[i]

否则f[i]=min(Σf[son[i]],dis[i])

虚树见上一篇博客:http://http://blog.csdn.net/thy_asdf/article/details/50387136


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define min(a,b) (a<b?a:b)
typedef long long ll;
const int maxn=250010,maxm=500010,maxk=22;
const ll inf=1ll<<50;
using namespace std;
int n,m,fa[maxn][maxk],dep[maxn],cnt,poi[maxn],stk[maxn],top,dfn[maxn],tim;ll f[maxn],dis[maxn];
bool bo[maxn];
bool cmp(int a,int b){return dfn[a]<dfn[b];}

struct Tgraph{
	int pre[maxm],now[maxn],son[maxm],val[maxm],tot;
	void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
	void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
	void dfs1(int x){
		dfn[x]=++tim;
		for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
		for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0])
			dep[son[y]]=dep[x]+1,dis[son[y]]=min(dis[x],val[y]),fa[son[y]][0]=x,dfs1(son[y]);
	}
	void dfs2(int x){
		f[x]=dis[x];ll sum=0;
		for (int y=now[x];y;y=pre[y]) dfs2(son[y]),sum+=f[son[y]];
		if (sum&&!bo[x]) f[x]=min(f[x],sum);
		now[x]=0;
	}
}g1,g2;

int lca(int a,int b){
	if (dep[a]<dep[b]) swap(a,b);
	for (int h=dep[a]-dep[b],i=20;i>=0;i--) if (h>=(1<<i)) h-=(1<<i),a=fa[a][i];
	if (a==b) return a;
	for (int i=20;i>=0;i--) if (fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
	return fa[a][0];
}

void work(){
	top=0;
	for (int i=1;i<=cnt;i++){
		if (!top){stk[++top]=poi[i];continue;}
		int u=lca(stk[top],poi[i]);
		while (dfn[u]<dfn[stk[top]]){
			if (dfn[u]>=dfn[stk[top-1]]){
				g2.add(u,stk[top]);
				if (stk[--top]!=u) stk[++top]=u;
				break;
			}
			g2.add(stk[top-1],stk[top]),top--;
		}
		stk[++top]=poi[i];
	}
	while (top>1) g2.add(stk[top-1],stk[top]),top--;
	g2.dfs2(stk[1]),printf("%lld\n",f[stk[1]]);
	for (int i=1;i<=cnt;i++) bo[poi[i]]=0;g2.tot=0;
}

int main(){
	scanf("%d",&n),dis[1]=inf;
	for (int i=1,a,b,c;i<n;i++) scanf("%d%d%d",&a,&b,&c),g1.add(a,b,c),g1.add(b,a,c);
	g1.dfs1(1),scanf("%d",&m);
	for (int i=1;i<=m;i++){
		scanf("%d",&cnt);
		for (int j=1;j<=cnt;j++) scanf("%d",&poi[j]),bo[poi[j]]=1;
		sort(poi+1,poi+1+cnt,cmp),work();
	}
	return 0;
}


转载于:https://www.cnblogs.com/thythy/p/5493492.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值