bzoj3991 寻宝游戏 dfs&set

       考虑关键点(有宝藏的点)及其lca构成的虚树,由于最后还需要回到原点,因此答案相当于虚树中所有边权的和的两倍。

       考虑树的边权的两倍怎么求,实际上就是按dfs序排序之后第一个点和第二个点,第二个点和第三个点……最后一个点和第一个点的距离的和。那么用set维护dfs序,插入和删除的时候统计一下就好了。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#define N 100005
#define inf 1000000000
#define ll long long
using namespace std;

set<int> s;
int n,m,tot,dfsclk,dep[N],fst[N],pnt[N<<1],nxt[N<<1],pos[N],id[N],fa[N][17],a[N],bin[25];
ll d[N],len[N<<1];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y,ll z){
	pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
	pos[x]=++dfsclk; id[dfsclk]=x; int p,i;
	for (i=1; bin[i]<=dep[x]; i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x][0]){
			fa[y][0]=x; d[y]=d[x]+len[p];
			dep[y]=dep[x]+1; dfs(y);
		}
	}
}
int lca(int x,int y){
	if (dep[x]<dep[y]) swap(x,y); int tmp=dep[x]-dep[y],i;
	for (i=0; bin[i]<=tmp; i++)
		if (tmp&bin[i]) x=fa[x][i];
	for (i=16; i>=0; i--)
		if (fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; }
	return (x==y)?x:fa[x][0];
}
ll dist(int x,int y){ return d[x]+d[y]-(d[lca(x,y)]<<1); }
int main(){
	n=read(); m=read(); int i;
	bin[0]=1; for (i=1; i<=17; i++) bin[i]=bin[i-1]<<1;
	for (i=1; i<n; i++){
		int x=read(),y=read(),z=read();
		add(x,y,(ll)z); add(y,x,(ll)z);
	}
	dfs(1); ll ans=0,tmp;
	s.insert(-inf); s.insert(inf); set<int>::iterator it;
	for (i=1; i<=m; i++){
		int k=read(),t;
		if (a[k]){ s.erase(pos[k]); t=-1; }
		else{ s.insert(pos[k]); t=1; }
		a[k]^=1; it=s.upper_bound(pos[k]);
		int r=*it,l=*(--it); if (l>=pos[k]) l=*(--it);
		if (l!=-inf) ans+=dist(id[l],k)*t; if (r!=inf) ans+=dist(id[r],k)*t;
		if (l!=-inf && r!=inf) ans-=dist(id[l],id[r])*t;
		tmp=(s.size()>3)?dist(id[*s.upper_bound(-inf)],id[*--s.lower_bound(inf)]):0;
		printf("%lld\n",ans+tmp);
	}
	return 0;
}


by lych

2016.3.6

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值