caioj1443:第k小的数Ⅲ
【传送门:caioj1443】
简要题意:
给出一颗n个点的树,给出每个点的权值,再给出n-1条边,有m个询问,每个询问输入x,y,k,输出第x节点到第y节点的路径上第k大的点
题解:
这是一道主席树的例题,感觉很想用树链剖分,但是会超时吧......
做法就是将每个点到树的根所形成的链建立线段树,然后dfs找出点与点之间的父子关系,每个点都将自己和自己的父亲,自己的父亲的父亲....直到根所组成的所有线段树合并起来,然后就可以得到能够代表区间的主席树了,然后每当询问输入x,y时,就先找x和y的最近公共祖先(倍增LCA来求),然后x和y的路径其实就是x到根的路径加上y到根的路径,然后减去最近公共祖先到根的路径,再减去最近公共祖先的父亲到根的路径(为什么是最近公共祖先的父亲呢,因为如果是再减去最近公共祖先到根的路径的话,最近公共祖先就会被除去,这样是不对的,所以减去最近公共祖先的父亲到根的路径)
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int s[110000],ss[110000]; struct trnode { int lc,rc,c; }tr[2100000];int cnt; int rt[110000]; struct edge { int x,y,next; }a[210000];int len,last[110000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int n; int LS(int d) { int l=1,r=n,ans=0; while(l<=r) { int mid=(l+r)/2; if(s[mid]<=d) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } void Link(int &u,int l,int r,int p) { if(u==0) u=++cnt; tr[u].c++; if(l==r) return ; int mid=(l+r)/2; if(p<=mid) Link(tr[u].lc,l,mid,p); else Link(tr[u].rc,mid+1,r,p); } void Merge(int &u1,int u2) { if(u1==0){u1=u2;return ;} if(u2==0) return ; tr[u1].c+=tr[u2].c; Merge(tr[u1].lc,tr[u2].lc); Merge(tr[u1].rc,tr[u2].rc); } int dep[110000]; int f[110000][21]; int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[x]-dep[y]>=(1<<i)) x=f[x][i]; if(x==y) return x; for(int i=20;i>=0;i--) if(dep[x]>=(1<<i)&&f[x][i]!=f[y][i]) { x=f[x][i];y=f[y][i]; } return f[x][0]; } void bt(int x,int fa) { dep[x]=dep[fa]+1; f[x][0]=fa; for(int i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1]; Merge(rt[x],rt[fa]); for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa) { bt(y,x); } } } int Ask(int u1,int u2,int u3,int u4,int l,int r,int p) { if(l==r) return s[l]; int c=tr[tr[u1].lc].c+tr[tr[u2].lc].c-tr[tr[u3].lc].c-tr[tr[u4].lc].c; int mid=(l+r)/2; if(p<=c) return Ask(tr[u1].lc,tr[u2].lc,tr[u3].lc,tr[u4].lc,l,mid,p); else return Ask(tr[u1].rc,tr[u2].rc,tr[u3].rc,tr[u4].rc,mid+1,r,p-c); } int main() { int m; scanf("%d%d",&n,&m); cnt=0;memset(rt,0,sizeof(rt)); for(int i=1;i<=n;i++) { scanf("%d",&ss[i]); s[i]=ss[i]; } sort(s+1,s+n+1); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y);ins(y,x); } cnt=0; for(int i=1;i<=n;i++) Link(rt[i],1,n,LS(ss[i])); dep[0]=0;bt(1,0); for(int i=1;i<=m;i++) { int x,y,k; scanf("%d%d%d",&x,&y,&k); int lca=LCA(x,y); printf("%d\n",Ask(rt[x],rt[y],rt[lca],rt[f[lca][0]],1,n,k)); } return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚