4539: [Hnoi2016]树
Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 510 Solved: 194
[ Submit][ Status][ Discuss]
Description
小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)后,得到新的
大树如下图所示
现在他想问你,树中一些结点对的距离是多少。
Input
第一行三个整数: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
Output
输出Q行,每行一个整数,第 i行是第 i个询问的答案。
Sample Input
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3
Sample Output
3
3
HINT
经过两次操作后,大树变成了下图所示的形状:
结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。
Source
对于这个题,我不想发表什么意见了注意longlong,切记,注意longlong!!!
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 100005
using namespace std;
typedef long long ll;
struct node{
int lc,rc,cnt;
}seg[maxn*20];
struct kuai{
ll l,r;
int rt;
kuai(){}
kuai(ll l,ll r,int rt):l(l),r(r),rt(rt){}
int operator<(const kuai& k)const{return r<k.r;}
}op[maxn];
struct edge{
int r,nxt;
}e[maxn<<1],xe[maxn<<1];
struct data{
int conn,ra;
data(){}
data(int conn,int ra):conn(conn),ra(ra){}
};
int fa[maxn][20],ptr,head[maxn],root[maxn],esz,dep[maxn],dfn[maxn],tot,size[maxn],top,tim[maxn],n,m,q;
ll xdist[maxn][20],dist[maxn],now;
data xfa[maxn][20];
void modify(int rt1,int &rt2,int l,int r,int k){
rt2=++ptr;
seg[rt2]=seg[rt1];
seg[rt2].cnt++;
if(l==r)return ;
int mid=l+r>>1;
if(k<=mid)modify(seg[rt1].lc,seg[rt2].lc,l,mid,k);
else modify(seg[rt1].rc,seg[rt2].rc,mid+1,r,k);
}
int query(int rt1,int rt2,int l,int r,int k){
if(l==r)return l;
int sz=seg[seg[rt2].lc].cnt-seg[seg[rt1].lc].cnt;
int mid=l+r>>1;
if(sz>=k)return query(seg[rt1].lc,seg[rt2].lc,l,mid,k);
else return query(seg[rt1].rc,seg[rt2].rc,mid+1,r,k-sz);
}
int dfs(int u,int f,int d){
fa[u][0]=f,dist[u]=d;
dfn[++top]=u,tim[u]=top,size[u]=1;
for(int i=1;i<20;++i)fa[u][i]=fa[fa[u][i-1]][i-1];
for(int t=head[u];t;t=e[t].nxt)if(e[t].r!=f)size[u]+=dfs(e[t].r,u,d+1);
return size[u];
}
int getid(int rt,int k){
return query(root[tim[rt]-1],root[tim[rt]+size[rt]-1],1,n,k);
}
int getkuai(ll u){
return lower_bound(op+1,op+tot+1,kuai(0,u,0))-op;
}
void addedge(int u,int v){
e[++esz].r=v;e[esz].nxt=head[u];head[u]=esz;
}
ll lca(int u,int v){
ll ans=dist[u]+dist[v];
if(dist[u]<dist[v])swap(u,v);
int d=dist[u]-dist[v];
for(int i=0;d>>i;i++)
if((d>>i)&1)u=fa[u][i];
if(u==v)return ans-2*dist[u];
for(int i=19;i>=0;--i)if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
u=fa[u][0];
return ans-2*dist[u];
}
ll solve(ll u,ll v){
ll ans=0;
bool flag=0;
int ku=getkuai(u),kv=getkuai(v);
if(dep[ku]<dep[kv])swap(ku,kv),swap(u,v);
u=getid(op[ku].rt,u-op[ku].l+1);
v=getid(op[kv].rt,v-op[kv].l+1);
if(ku==kv)return lca(u,v);
int d=dep[ku]-dep[kv];
for(int i=0;d>>i;i++)if((d>>i)&1){
ans+=dist[u]-dist[op[ku].rt];
u=xfa[ku][i].conn;
ans+=xdist[ku][i];
ku=xfa[ku][i].ra;
}
if(ku==kv)return lca(u,v)+ans;
ans+=dist[u]-dist[op[ku].rt],u=op[ku].rt;
ans+=dist[v]-dist[op[kv].rt],v=op[kv].rt;
for(int i=19;i>=0;--i)if(xfa[ku][i].ra!=xfa[kv][i].ra){
ans+=dist[u]-dist[op[ku].rt],u=op[ku].rt;
ans+=dist[v]-dist[op[kv].rt],v=op[kv].rt;
u=xfa[ku][i].conn,v=xfa[kv][i].conn;
ans+=xdist[ku][i]+xdist[kv][i];
ku=xfa[ku][i].ra,kv=xfa[kv][i].ra;
flag=true;
}
ans+=2;
if(flag)ans+=dist[u]-dist[op[ku].rt]+dist[v]-dist[op[kv].rt];
u=xfa[ku][0].conn,v=xfa[kv][0].conn;
return ans+lca(u,v);
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1,u,v;i<n;++i)
scanf("%d%d",&u,&v),addedge(u,v),addedge(v,u);
dfs(1,0,1);
for(int i=1;i<=n;++i)modify(root[i-1],root[i],1,n,dfn[i]);
op[++tot]=kuai(1,size[1],1);
now=size[1];
ll v;
int u;
for(int i=1;i<=m;++i){
scanf("%d%lld",&u,&v);
int ra=getkuai(v);
int conn=getid(op[ra].rt,v-op[ra].l+1);
op[++tot]=kuai(now+1,now+size[u],u),now+=size[u];
xfa[tot][0]=data(conn,ra),xdist[tot][0]=1;
dep[tot]=dep[ra]+1;
}
for(int i=1;i<20;++i)
for(int j=1;j<=tot;++j){
xfa[j][i]=xfa[xfa[j][i-1].ra][i-1];
xdist[j][i]=xdist[j][i-1]+dist[xfa[j][i-1].conn]-dist[op[xfa[j][i-1].ra].rt]+xdist[xfa[j][i-1].ra][i-1];
}
for(int i=1;i<=q;++i){
ll u,v;
scanf("%lld%lld",&u,&v);
printf("%lld\n",solve(u,v));
}
}