【题解】P6071 『MdOI R1』Treequery

题目描述

给定一棵 n 个点的无根树,边有边权。

E(x,y) 表示树上 x,y 之间的简单路径上的所有边的集合,特别地,当 x=y 时,E(x,y)=

你需要 实时 回答 q 个询问,每个询问给定 p,l,r,请你求出集合 i=lrE(p,i) 中所有边的边权和,即 E(p,lr) 的交所包含的边的边权和。

通俗的讲,你需要求出 p[l,r] 内每一个点的简单路径的公共部分长度。

题解

套路题感觉没什么好讲的。
既然是公共部分长度,那么只有所有点都在当前点子树内或者都不在才有解。
对于在当前点子树内,答案为l到r中dfs虚最小和最大的两点到当前点的距离。
对于其他的,即是找到当前点向上第一次与l到r点构成的虚树相交的位置,查找dfs序上前驱后继和当前点的lca即可。
提示:树上问题与dfs序一般有优美的性质。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=1000010;
int rt[N],tr[N*40],ls[N*40],rs[N*40],tot;
int head[N],to[N*2],fro[N*2],w[N*2],tail;
int n,q,dep[N],tort[N],fa[N][21];
int dfn[N],siz[N],bel[N],cnt;
inline void adlin(int x,int y,int z){
	to[++tail]=y,fro[tail]=head[x],head[x]=tail,w[tail]=z;
	return ;
}
void dfs(int u,int Fa){
	dfn[++cnt]=u,bel[u]=cnt,siz[u]=1;
	fa[u][0]=Fa;
	dep[u]=dep[Fa]+1;
	for(int k=1;(1<<k)<dep[u];k++)fa[u][k]=fa[fa[u][k-1]][k-1];
	for(int k=head[u];k;k=fro[k]){
		int v=to[k];
		if(v==Fa)continue;
		else tort[v]=tort[u]+w[k],dfs(v,u),siz[u]+=siz[v];
	}
	return ;
}
inline int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int k=20;k>=0;k--){
		if(dep[fa[x][k]]>=dep[y])x=fa[x][k];
	}
	for(int k=20;k>=0;k--){
		if(fa[x][k]!=fa[y][k])x=fa[x][k],y=fa[y][k];
	}
	if(x!=y)x=fa[x][0];
	return x;
}
inline void update(int u){
	tr[u]=tr[ls[u]]+tr[rs[u]];
	return ;
}
void modify(int &u,int v,int l,int r,int aim){
	u=++tot;
//	cout<<u<<" "<<v<<"-"<<l<<" "<<r<<"\n";
	if(l==r){
//		cout<<u<<":"<<l<<" "<<r<<":"<<1<<"\n";
		return tr[u]=1,void(0);
	}
	int mid=(l+r)/2;
	if(aim<=mid)rs[u]=rs[v],modify(ls[u],ls[v],l,mid,aim);
	else ls[u]=ls[v],modify(rs[u],rs[v],mid+1,r,aim);
	update(u);
//	cout<<u<<" "<<v<<":"<<l<<" "<<r<<"-"<<ls[u]<<" "<<rs[u]<<":"<<tr[u]<<"\n";
	return ;
}
int querysm(int u,int v,int l,int r,int L,int R){
	if(!u||tr[u]-tr[v]==0)return 0;
	if(L<=l&&r<=R)return tr[u]-tr[v];
	int mid=(l+r)/2,ansn=0;
	if(L<=mid)ansn+=querysm(ls[u],ls[v],l,mid,L,R);
	if(R>mid)ansn+=querysm(rs[u],rs[v],mid+1,r,L,R);
	return ansn;
}
int queryrk(int u,int v,int l,int r,int k){
	if(!u||tr[u]-tr[v]==0)return 0;
	if(l==r)return l;
	int mid=(l+r)/2;
	if(tr[ls[u]]-tr[ls[v]]>=k)return queryrk(ls[u],ls[v],l,mid,k);
	else return queryrk(rs[u],rs[v],mid+1,r,k-(tr[ls[u]]-tr[ls[v]]));
}
//void debug(int u,int l,int r){
//	if(!u)return ;
//	cout<<u<<":"<<l<<" "<<r<<"-"<<ls[u]<<" "<<rs[u]<<":"<<tr[u]<<"\n";
//	if(l==r)return ;
//	int mid=(l+r)/2;
//	debug(ls[u],l,mid),debug(rs[u],mid+1,r);
//	return ; 
//}
int calfir(int p,int l,int r){
	if(l<=p&&p<=r)return 0;
	int fros=querysm(rt[r],rt[l-1],1,n,1,bel[p]);
	int x=dfn[queryrk(rt[r],rt[l-1],1,n,fros+1)];
	int y=dfn[queryrk(rt[r],rt[l-1],1,n,fros+r-l+1)];
	x=lca(x,y);
	return tort[x]-tort[p];
}
signed main(){
//	freopen("P6071_8.in","r",stdin);
//	freopen("ans.out","w",stdout); 
	n=rd(),q=rd();
	for(int i=1;i<n;i++){
		int x=rd(),y=rd(),z=rd();
		adlin(x,y,z),adlin(y,x,z);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)modify(rt[i],rt[i-1],1,n,bel[i]);
//	for(int i=1;i<=n;i++)cout<<rt[i]<<" ";
//	cout<<"\n";
//	debug(rt[2],1,n),cout<<"\n";
//	for(int i=1;i<=n;i++)cout<<i<<":"<<dep[i]<<" "<<tort[i]<<":"<<siz[i]<<"-"<<bel[i]<<"\n";
	int lastans=0;
	for(int t=1;t<=q;t++){
//		cerr<<t<<":";
		int p=rd()^lastans,l=rd()^lastans,r=rd()^lastans;
		int intr=querysm(rt[r],rt[l-1],1,n,bel[p],bel[p]+siz[p]-1);
//		cerr<<"T:"<<t<<":"<<p<<"-"<<l<<" "<<r<<":"<<intr<<"\n";
		if(l<=p&&p<=r){
			printf("%d\n",lastans=0);
//			cerr<<"sub=1\n";
//			cerr<<lastans<<"\n";
			continue;
		}
		else if(intr==r-l+1){
			printf("%d\n",lastans=calfir(p,l,r));
//			cerr<<"sub=2\n";
//			cerr<<lastans<<"\n";
		}
		else if(intr==0){
			int fros=querysm(rt[r],rt[l-1],1,n,1,bel[p]);
			int x=queryrk(rt[r],rt[l-1],1,n,fros);
			int y=queryrk(rt[r],rt[l-1],1,n,fros+1);
			x=dfn[x],y=dfn[y];
			x=lca(x,p),y=lca(y,p);
//			cout<<"find:"<<x<<" "<<y<<"\n";
			if(dep[x]<dep[y])x=y;
			lastans=tort[p]-tort[x];
			intr=querysm(rt[r],rt[l-1],1,n,bel[x],bel[x]+siz[x]-1);
			if(intr==r-l+1)lastans+=calfir(x,l,r);
			printf("%d\n",lastans);
//			cerr<<"sub=3\n";
//			cerr<<lastans<<"\n";
		}
		else printf("%d\n",lastans=0);
	}
	return 0;
}
posted @   flywatre  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示