好题。
算法: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;
}