zsyzlzy

导航

 

好题。

算法:dfs序,主席树(可持久化线段树),LCA。

题目

代码+解析

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define g getchar()
using namespace std;
typedef long long ll;
const int N=2e5+10;
template<class o>void qr(o&x) {
	char c=g;int f=1;x=0;
	while(!isdigit(c)){if(c=='-')f=-1;c=g;}
	while(isdigit(c))x=x*10+c-'0',c=g;
	x*=f;
}
template<class o>void write(o x) {
	if(x/10)write(x/10);
	putchar(x%10+'0');
}
template<class o>void pri(o x) {
	if(x<0)x=-x,putchar('-');
	write(x);puts("");
}

int n,m,fa[N][20],dis[N],dep[N],in[N],ou[N],q[N],tot,ans;

struct edge{int y,next,d;}a[N<<1]; int len,last[N];
void ins(int x,int y,int d) {a[++len]=(edge){y,last[x],d};last[x]=len;}

void dfs(int x) {
	in[x]=++tot; q[tot]=x;
	for(int k=last[x];k;k=a[k].next) {
		int y=a[k].y; if(y==fa[x][0]) continue;
		fa[y][0]=x; for(int i=1;fa[y][i-1];i++) fa[y][i]=fa[fa[y][i-1]][i-1];
		dis[y]=dis[x]+a[k].d; dep[y]=dep[x]+1; dfs(y);
	}
	ou[x]=tot;
}

int lc[N*20],rc[N*20],c[N*20],cnt,root[N];

void update(int &x,int y,int l,int r,int pos) {
	lc[x=++cnt]=lc[y]; rc[x]=rc[y]; c[x]=c[y]+1; 
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(pos<=mid) update(lc[x],lc[y],l,mid,pos);
	else 		 update(rc[x],rc[y],mid+1,r,pos);
}

int Qmin(int x,int y,int l,int r,int L,int R) {//在原始询问[l,r]中找到dfs序[L,R]内最小的数
	 if(c[x]==c[y]||L>R) return -1;
	 if(l==r) return q[l];
	 int mid=(l+r)>>1,t=-1;
	 if(L<=mid) t=Qmin(lc[x],lc[y],l,mid,L,R);
	 if(mid<R&&t==-1) t=Qmin(rc[x],rc[y],mid+1,r,L,R);
	 return t;
} 

int Qmax(int x,int y,int l,int r,int L,int R) {
	if(c[x]==c[y]||L>R) return -1;
	if(l==r) return q[l];
	int mid=(l+r)>>1,t=-1;
	if(mid<R) t=Qmax(rc[x],rc[y],mid+1,r,L,R);
	if(L<=mid&&t==-1) t=Qmax(lc[x],lc[y],l,mid,L,R);
	return t;
}

bool check(int x,int y) {//x是否在y子树内
	return in[y]<=in[x]&&in[x]<=ou[y];
}

int LCA(int x,int y) {
	if(dep[x]>dep[y]) swap(x,y);
	for(int k=dep[y]-dep[x],i=0;k;i++)
		if(k>>i&1) y=fa[y][i],k^=1<<i;
	if(x==y) return x;
	for(int i=17;i>=0;i--)
		if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}

int solve(int p,int l,int r) {
	int x,y;
	int a=check(x=Qmin(root[r],root[l-1],1,n,1,n),p),
		b=check(y=Qmax(root[r],root[l-1],1,n,1,n),p);
	if(a^b)return 0;
	if(a) return dis[LCA(x,y)]-dis[p];
	a=Qmin(root[r],root[l-1],1,n,in[p],n),
	b=Qmax(root[r],root[l-1],1,n,1,in[p]-1);
	if(a==-1) a=x;
	if(b==-1) b=y;
	x=LCA(a,p); y=LCA(b,p);
	if(x==y) return dis[LCA(a,b)]+dis[p]-2*dis[x];
	else return dep[x]>dep[y]?dis[p]-dis[x]:dis[p]-dis[y];
	/*
	总共有三种情况:
	1.有在p子树内的,又不在的,输出0.
	2.全在p子树内的,输出LCA(l...r)->p.根据dfs序的性质可知:答案即为LCA(dfsmin,dfsmax)->p.
	3.全在外。要么LCA(l...r,p)都相等,要么不等。分情况讨论即可。
	
	为了方便求出[l,r]的dfs序哪个位置最大,哪个最小。
	我们把叶子节点定为dfs序。用可持久化线段树维护即可。 
	*/
}

int main() {
	qr(n); qr(m);
	for(int i=1,x,y,d;i<n;i++)
		qr(x),qr(y),qr(d),ins(x,y,d),ins(y,x,d);
	dfs(1);
	for(int i=1;i<=n;i++) update(root[i],root[i-1],1,n,in[i]);
	while(m--) {
		int p,l,r; qr(p); qr(l); qr(r);
		p^=ans; l^=ans; r^=ans;
		pri(ans=solve(p,l,r));
	}
	return 0;
}
posted on 2020-02-15 11:14  zsyzlzy  阅读(131)  评论(0编辑  收藏  举报