BZOJ2286: [Sdoi2011]消耗战 虚树

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2286

题解:看数据范围以及结合题目要求显然可以用虚树,然后建出来虚树在上面DP就好了

建虚树时以1为根,然后f[i]表示将以i为节点的子树(包括其自己)断开的费用,如果f[i]为有资源的点f[i]=min(w[1到i的路径]),如果i没有资源,就将f[i]再和sum[f[son[i]]]取个min就OK。

(代码一看就懂)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const LL inf=1e12;
const int N=250005;
const int M=500005;
int n,m,K,d[N*2],s[N*2],top;
int cnt,to[M],nxt[M],lj[N],w[M];
int cnt1,to1[M],nxt1[M],lj1[N];
void ins(int f,int t,int p) {cnt++,to[cnt]=t,nxt[cnt]=lj[f],lj[f]=cnt,w[cnt]=p;}
void add(int f,int t,int p) {ins(f,t,p),ins(t,f,p);}
void ins1(int f,int t) {cnt1++,to1[cnt1]=t,nxt1[cnt1]=lj1[f],lj1[f]=cnt1;}
void add1(int f,int t)
{
	if(f==t) return;
	ins1(f,t);
	ins1(t,f);
}
int fa[N],dep[N],anc[N][20],pos[N],tar,Log[N];
LL dis[N];
bool cmp(int x,int y) {return pos[x]<pos[y];}
void dfs(int x)
{
	pos[x]=++tar;
	dep[x]=dep[fa[x]]+1;
	for(int i=lj[x];i;i=nxt[i])
	if(to[i]!=fa[x])
	{
		fa[to[i]]=x;
		anc[to[i]][0]=x;
		dis[to[i]]=min(dis[x],(LL)w[i]);
		dfs(to[i]);
	}
}
void pre()
{
	Log[0]=-1;
	for(int i=1;i<=n;i++) Log[i]=Log[i>>1]+1;
	for(int j=1;j<=Log[n];j++)
	for(int i=1;i<=n;i++)
	anc[i][j]=anc[anc[i][j-1]][j-1]; 
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	int deep=dep[x]-dep[y];
	for(int i=0;i<=Log[deep];i++)
	if(deep&(1<<i)) x=anc[x][i];
	if(x==y) return x;
	for(int i=Log[n];i>=0;i--)
	if(anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
	return fa[x];
}
LL f[N];
bool b[N];
void dfs1(int x,int fa)
{
	LL sum=0;
	for(int i=lj1[x];i;i=nxt1[i])
	if(to1[i]!=fa)
	{
		dfs1(to1[i],x);
		sum+=f[to1[i]];
	}
	if(b[x]) f[x]=dis[x];
	else f[x]=min(sum,dis[x]);
	lj1[x]=b[x]=0;
}
int main()
{
	scanf("%d",&n);
	int x,y,z;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	dis[1]=inf;
	dfs(1);
	pre();
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d",&K);
		for(int i=1;i<=K;i++) scanf("%d",&d[i]),b[d[i]]=true;
		sort(d+1,d+K+1,cmp);
		top=cnt1=0;
		s[++top]=1;
		for(int i=1;i<=K;i++)
		{
			int tmp=lca(s[top],d[i]);
			while(top>1&&dep[s[top-1]]>=dep[tmp])
			{
				add1(s[top-1],s[top]);
				top--; 
			}
			add1(s[top],tmp);
			s[top]=tmp;
			s[++top]=d[i];
		}
		for(int i=1;i<top;i++) add1(s[i],s[i+1]);
		dfs1(1,0);
		printf("%lld\n",f[1]);
	}
}


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值