小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了。开始,小A只有一棵结点数为N的树,结
点的编号为1,2,…,N,其中结点1为根;我们称这颗树为模板树。小A决定通过这棵模板树来构建一颗大树。构建过
程如下:(1)将模板树复制为初始的大树。(2)以下(2.1)(2.2)(2.3)步循环执行M次(2.1)选择两个数字a,b,
其中1<=a<=N,1<=b<=当前大树的结点数。(2.2)将模板树中以结点a为根的子树复制一遍,挂到大树中结点b的下
方(也就是说,模板树中的结点a为根的子树复制到大树中后,将成为大树中结点b的子树)。(2.3)将新加入大树
的结点按照在模板树中编号的顺序重新编号。例如,假设在进行2.2步之前大树有L个结点,模板树中以a为根的子
树共有C个结点,那么新加入模板树的C个结点在大树中的编号将是L+1,L+2,…,L+C;大树中这C个结点编号的大小
顺序和模板树中对应的C个结点的大小顺序是一致的。下面给出一个实例。假设模板树如下图:
根据第(1)步,初始的大树与模板树是相同的。在(2.1)步,假设选择了a=4,b=3。运行(2.2)和(2.3)后,得到新的
大树如下图所示
现在他想问你,树中一些结点对的距离是多少。
第一行三个整数:N,M,Q,以空格隔开,N表示模板树结点数,M表示第(2)中的循环操作的次数,Q 表示询问数
量。接下来N-1行,每行两个整数 fr,to,表示模板树中的一条树边。再接下来M行,每行两个整数x,to,表示将模
板树中 x 为根的子树复制到大树中成为结点to的子树的一次操作。再接下来Q行,每行两个整数fr,to,表示询问
大树中结点 fr和 to之间的距离是多少。N,M,Q<=100000
输出Q行,每行一个整数,第 i行是第 i个询问的答案。
经过两次操作后,大树变成了下图所示的形状:
结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。
正解:主席树+倍增lca
1 //It is made by ljh2000
2 #include <iostream>
3 #include <cstdlib>
4 #include <cstring>
5 #include <cstdio>
6 #include <cmath>
7 #include <algorithm>
8 using namespace std;
9 typedef long long LL;
10 const int MAXN = 200011;
11 int n,m,q,up[MAXN]/*每个块的根相连的点,对应的是在原树中的编号*/;
12 int root[MAXN];//每个块的根,对应的是在原树中的编号
13 int kcnt;//块的个数
14 int rt[MAXN];//主席树上的每个点的线段树结点编号的开始位置;
15 int D[MAXN];//每个块的根结点在原树中的dis
16 LL tot,ans,id[MAXN];
17 struct node{ int ls,rs,nn; }t[MAXN*20];
18 struct Tree{
19 int ecnt,first[MAXN],next[MAXN*2],to[MAXN*2],size[MAXN],f[MAXN][18];
20 int dfn[MAXN],pos[MAXN];
21 LL deep[MAXN],dis[MAXN],w[MAXN*2];
22 inline int jump(int x,int y){ for(int i=17;i>=0;i--) if(deep[f[x][i]]>deep[y]) x=f[x][i]; return x; }//调到lca的儿子结点
23 inline void link(int x,int y,LL z){
24 next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
25 next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
26 }
27
28 inline void dfs(int x,int fa){
29 size[x]=1; dfn[x]=++ecnt; pos[ecnt]=x;
30 for(int j=1;j<=17;j++) if(f[x][j-1]!=0) f[x][j]=f[f[x][j-1]][j-1]; else break;
31 for(int i=first[x];i;i=next[i]) {
32 int v=to[i]; if(v==fa) continue; f[v][0]=x;
33 deep[v]=deep[x]+1; dis[v]=dis[x]+w[i];
34 dfs(v,x); size[x]+=size[v];
35 }
36 }
37
38 inline int lca(int x,int y){
39 if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
40 for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return x;
41 for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
42 return f[x][0];
43 }
44
45 }t1,t2;
46
47 inline int getint(){
48 int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
49 if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
50 }
51
52 inline LL getlong(){
53 LL w=0;int q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
54 if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
55 }
56
57 inline int build(int k,int l,int r,int val){
58 tot++; t[tot]=t[k]; t[tot].nn++;
59 if(l==r) return tot; int cc=tot,mid=(l+r)>>1;
60 if(val<=mid) t[cc].ls=build(t[k].ls,l,mid,val);
61 else t[cc].rs=build(t[k].rs,mid+1,r,val);
62 return cc;
63 }
64
65 inline int query(int now,int from,int l,int r,int val){
66 if(l==r) return l; int mid=(l+r)>>1;
67 int cp=t[t[now].ls].nn-t[t[from].ls].nn;
68 if(val<=cp) return query(t[now].ls,t[from].ls,l,mid,val);
69 else return query(t[now].rs,t[from].rs,mid+1,r,val-cp);
70 }
71
72 inline int belong_block(LL x){ //查询所在的块的编号
73 if(x<=n) return 1;
74 int l=1,r=kcnt,mid; int cc=1;
75 while(l<=r) {
76 mid=(l+r)>>1;
77 if(x>=id[mid]) cc=mid,l=mid+1;
78 else r=mid-1;
79 }
80 return cc;
81 }
82
83 inline int kth_query(LL x,int kuai){ //查询在编号为kuai的块中编号为x的结点在原树中的实际编号
84 if(x<=n) return x;
85 x=x-id[kuai]+1; int R=root[kuai];
86 int l=t1.dfn[R];int r=l+t1.size[R]-1;//!!!
87 return query(rt[r],rt[l-1],1,n,x);
88 }
89
90 inline void work(){
91 n=getint(); m=getint(); q=getint(); int x_belong,y_belong; LL x,y;
92 for(int i=1;i<n;i++) { x=getint(); y=getint(); t1.link(x,y,1); }
93 t1.ecnt=0; t1.deep[1]=0; t1.dfs(1,0); tot=0;
94 for(int i=1;i<=n;i++) { rt[i]=build(rt[i-1],1,n,t1.pos[i]); }
95 kcnt=1; root[1]=1; id[1]=1; up[1]=0; D[0]=0; tot=n;
96 t2.ecnt=0;
97 for(int i=1;i<=m;i++) {
98 x=getint(); y=getlong(); y_belong=belong_block(y);//y所在的块的编号
99 kcnt++; D[kcnt]=t1.dis[x]; root[kcnt]=x; id[kcnt]=tot+1;
100 tot+=t1.size[x]; up[kcnt]=kth_query(y,y_belong);
101 t2.link(y_belong,kcnt,t1.dis[up[kcnt]]-D[y_belong]+1);//两个块相连的地方还有1的距离
102 }
103 t2.deep[1]=0; t2.ecnt=0; t2.dfs(1,0);
104 int Yx,Yy,Ylca;//在原树中的对应编号
105 int Xlca;//两个块在新树上的lca
106 for(int i=1;i<=q;i++) {
107 x=getlong(); y=getlong(); ans=0; x_belong=belong_block(x); y_belong=belong_block(y);
108 Yx=kth_query(x,x_belong); Yy=kth_query(y,y_belong); Ylca=t1.lca(Yx,Yy);
109 if(x_belong==y_belong) { ans=t1.dis[Yx]+t1.dis[Yy]-2*t1.dis[Ylca]; printf("%lld\n",ans); continue; }//已经处于同一块中
110 if(t2.deep[x_belong]<t2.deep[y_belong]) swap(x,y),swap(x_belong,y_belong),swap(Yx,Yy);
111 Xlca=t2.lca(x_belong,y_belong);
112 if(Xlca!=y_belong) {
113 ans=t1.dis[Yy]-D[y_belong]; ans++; y=t2.jump(y_belong,Xlca);
114 ans+=t2.dis[y_belong]-t2.dis[y];//块与块之间的距离直接累加,可以跨越
115 y=up[y];//往上走一步,走到lca代表的块中
116 }
117 else y=kth_query(y,y_belong);
118 ans+=t1.dis[Yx]-D[x_belong]; ans++; x=t2.jump(x_belong,Xlca);
119 ans+=t2.dis[x_belong]-t2.dis[x]; x=up[x]; Ylca=t1.lca(x,y);
120 ans+=t1.dis[x]+t1.dis[y]-2*t1.dis[Ylca];
121 printf("%lld\n",ans);
122 }
123 }
124
125 int main()
126 {
127 work();
128 return 0;
129 }