题目:
求树上两点之间第k小点权
题解:
对每个节点到根节点的路径建一棵线段树,这样每个点的线段树都从他父亲得到
对于询问(u,v),sum[u]+sum[v]-sum[lca]-sum[fa[lca]]可以表示u到v的路径
#include<cstdio> #include<algorithm> #include<cstring> #define N 200010 using namespace std; int root[N],fa[N],n,m,lastans,w[N],head[N],b[N],lim,pcnt,anc[N][25],ecnt,deep[N]; struct node {int lc,rc,sum;}t[N*20]; struct edge {int nxt,v;}e[2*N]; void add(int u,int v) { e[++ecnt].v=v;e[ecnt].nxt=head[u];head[u]=ecnt; e[++ecnt].v=u;e[ecnt].nxt=head[v];head[v]=ecnt; } void Insert(int x,int &y,int l,int r,int k) { t[y=++pcnt]=t[x];t[y].sum++; if (l==r) return ; int mid=l+r>>1; if (k<=mid) Insert(t[x].lc,t[y].lc,l,mid,k); else Insert(t[x].rc,t[y].rc,mid+1,r,k); } void dfs(int u) { deep[u]=deep[anc[u][0]]+1; Insert(root[anc[u][0]],root[u],1,lim,w[u]); for (int i=head[u],v;i;i=e[i].nxt) if (e[i].v!=anc[u][0]) anc[v=e[i].v][0]=u,dfs(v); } int lca(int x,int y) { if (deep[x]<deep[y]) swap(x,y); for (int i=20;i>=0;i--) if (deep[anc[x][i]]>=deep[y]) x=anc[x][i]; if (x==y) return x; for (int i=20;i>=0;i--) if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i]; return anc[x][0]; } int query(int u,int v,int k) { int GGfa=lca(u,v),tmp[4]={root[u],root[v],root[GGfa],root[anc[GGfa][0]]},val,l=1,r=lim,mid; while (l<r) { mid=l+r>>1; val=t[t[tmp[0]].lc].sum+t[t[tmp[1]].lc].sum-t[t[tmp[2]].lc].sum-t[t[tmp[3]].lc].sum; if (k<=val) { r=mid; for (int i=0;i<4;i++) tmp[i]=t[tmp[i]].lc; } else { l=mid+1;k-=val; for (int i=0;i<4;i++) tmp[i]=t[tmp[i]].rc; } } return b[l]; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",w+i),b[i]=w[i]; sort(b+1,b+n+1); lim=unique(b+1,b+1+n)-b-1; for (int i=1;i<=n;i++) w[i]=lower_bound(b+1,b+1+lim,w[i])-b; for (int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),add(u,v); dfs(1); for (int j=1;j<=20;j++) for (int i=1;i<=n;i++) anc[i][j]=anc[anc[i][j-1]][j-1]; for (int i=1,u,v,k;i<=m;i++) { scanf("%d%d%d",&u,&v,&k); printf("%d",lastans=query(u^lastans,v,k)); if (i<m) printf("\n"); } return 0; }