bzoj 2286: [Sdoi2011]消耗战

3 篇文章 0 订阅

题目大意,切掉若干条边,使根不能到指定的点,代价最小,多次询问。

这篇东西不错:点击打开链接

虚树的模板题,注意卡时,在建虚树前先将一条链的点删剩一个,每个点记录到根最短的边边权。

貌似数据有误?反正我开了long long 记录边权就A了。

LCA模板一定不能错啊啊啊啊!

code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const LL inf=(1LL<<56);
struct trnode{
	int dep,fa[25];
}tr[250010];int ys[250010],z=0;LL C[250010];
LL f[250010];
struct node{
	int y,next;
	LL c;
}a[500010];int last[250010],len;
int n,h[250010],num;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int LLread()
{
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void ins(int x,int y,LL c)
{
	a[++len].y=y;a[len].c=c;
	a[len].next=last[x];last[x]=len;
}
void dfs(int x,int fa,LL c)
{
	ys[x]=++z;tr[x].fa[0]=fa;tr[x].dep=tr[fa].dep+1;C[x]=c;
	for(int i=1;(1<<i)<=tr[x].dep;i++)
		tr[x].fa[i]=tr[tr[x].fa[i-1]].fa[i-1];
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y!=fa) dfs(y,x,min(a[i].c,c));
	}
}
int findlca(int x,int y)
{
	if(tr[x].dep<tr[y].dep) swap(x,y);
	for(int i=19;i>=0;i--)
		if((1<<i)<=tr[x].dep-tr[y].dep) x=tr[x].fa[i];
	if(x==y) return x;
	for(int i=19;i>=0;i--)
		if((1<<i)<=tr[x].dep&&tr[x].fa[i]!=tr[y].fa[i])
			x=tr[x].fa[i],y=tr[y].fa[i];
	return tr[x].fa[0];
}
bool cmp(int a,int b){return ys[a]<ys[b];}
int sta[250010],top;
void dfs(int x)
{
	f[x]=(LL)C[x];
	LL s=0;
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		dfs(y);s+=f[y];
	}
	if(last[x]) f[x]=min(f[x],s);
	last[x]=0;
}
void build()
{
	top=0;sort(h+1,h+num+1,cmp);
	sta[++top]=1;int tot=num;num=1;
	for(int i=2;i<=tot;i++)
	{
		int lca=findlca(h[num],h[i]);
		if(lca!=h[num]) h[++num]=h[i];
	}
	len=0;
	for(int i=1;i<=num;i++)
	{
		int x=h[i],lca=findlca(sta[top],x);
		if(lca==sta[top]) sta[++top]=x;
		else
		{
			while(top>=2&&tr[sta[top-1]].dep>=tr[lca].dep)
			{
				ins(sta[top-1],sta[top],1LL);
				top--;
			}
			if(lca!=sta[top])
			{
				ins(lca,sta[top],1LL);
				sta[top]=lca;
			}
			sta[++top]=x;
		}
	}
	for(int i=1;i<top;i++) ins(sta[i],sta[i+1],1);
	dfs(1);
	printf("%lld\n",f[1]);
}
int main()
{
	n=read();
	memset(last,0,sizeof(last));len=0;
	for(int i=1;i<n;i++)
	{
		int x,y,c;x=read();y=read();c=LLread();
		ins(x,y,c);ins(y,x,c);
	}
	tr[0].dep=-1;dfs(1,0,inf);
	int T;T=read();
	memset(last,0,sizeof(last));
	while(T--)
	{
		num=read();
		for(int i=1;i<=num;i++) h[i]=read();
		build();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值