P4055[Hnoi2016 day1]树 | ||
|
小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个询问的答案。
5 2 3
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3
6
3
3
分析:
1.每一次新增的子树缩点,视为缩到这一坨点的根节点处
2.缩点后构建新图,点与点之间边的权值为这两坨点的根节点的距离
3.还必须处理出相连的两坨点到底是哪两个原图中的点相连,不然2.中的距离你怎么算?
处理这个需要用到可持久化线段树,求区间第k小。至于如何转化为区间第k小的:先对原树求dfs序,显然每一个子树对应dfs序上连续一段。现在给你一个新树上的点x,很容易用二分查找找到属于哪一个缩了之后的点,由于新树上点的编号连续且编号大小顺序对应原树上 的大小顺序,那么x剪掉之前的每一坨点的个数得到数是什么呢?就是这个点在其所在的坨(缩了的一坨点)中的标号的排名k,就转化为了主席树求区间第k大
4.现在我们来看询问。假设给定两个点x,y。
若x,y属于同一坨点,显然直接输出他们在原树上的最短距离
若不属于同一坨,在缩了点的新树上找其所在坨之间的距离,加上其自身与其所在坨的根节点的距离。但这还没完,因为找缩了点后的lca那个地方的距离是不对的,两边均是加的直接到那一坨根节点的距离,那么减掉这一部分,又由于可以用之前的预处理得到,路径穿入穿出这一坨经过的是那两个点,再加上这两个点在原树上的最短距离即可。
5.代码写的还是比较浅显易懂,静下心来看一会儿就懂了
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
template
inline void _read(T& x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9'){if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
int n,m,q,tot,clk;
int dfn[100005];
int SIZE[5000005],ls[5000005],rs[5000005],root[100005],Root[100005];
int id[100005],R[100005];
void insert(int pre,int &p,int l,int r,int x){
p=++tot;SIZE[p]=SIZE[pre]+1;
if(l==r)return;
int mid=(l+r)>>1;
ls[p]=ls[pre];rs[p]=rs[pre];
if(x<=mid)insert(ls[pre],ls[p],l,mid,x);
else insert(rs[pre],rs[p],mid+1,r,x);
}
void insert(int id,int v){insert(Root[id-1],Root[id],1,n,v);/*cout<<"insert:"<>1;
if(SIZE[ls[p]]-SIZE[ls[pre]]>=k)return query(ls[pre],ls[p],l,mid,k);
else return query(rs[pre],rs[p],mid+1,r,k-(SIZE[ls[p]]-SIZE[ls[pre]]));
}
int query(int x,int y,int k){return query(Root[x-1],Root[y],1,n,k);}
struct line{
int from,to,len;
line(){}
line(int x,int y,int z){from=x;to=y;len=z;}
};
struct graph{
line edge[200005];
int last[100005],_next[200005];
int fa[100005][20],dep[100005];
ll dis[100005],size[100005];
int e,vistime;
void init(){
e=vistime=0;
memset(fa,0,sizeof(fa));
memset(last,0,sizeof(last));
memset(_next,0,sizeof(_next));
}
void add_edge(int x,int y,int z){
edge[++e]=line(x,y,z);
_next[e]=last[x];
last[x]=e;
}
void dfs(int x){
//cout<<"dfs:"<=0;i--){
if(step&(1<=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];y=fa[y][i];
}
}
return fa[x][0];
}
ll dist(int x,int y){
return dis[x]+dis[y]-2ll*dis[lca(x,y)];
}
int up(int x,int y){
go_up(x,dep[x]-dep[y]-1);
return x;
}
};
graph ori,newg;
ll sum=0,cnt[200005];
int from[100005];
int pos;
int getid(ll x){
return lower_bound(cnt+1,cnt+1+pos,x)-cnt;
}
void query(ll a,ll b){
int ida=getid(a),roota=root[ida],aa=query(id[roota],R[roota],a-cnt[ida-1]);
int idb=getid(b),rootb=root[idb],bb=query(id[rootb],R[rootb],b-cnt[idb-1]);
int lca=newg.lca(ida,idb);
if(ida==idb){
printf("%lld\n",ori.dist(aa,bb));
return;
}
ll res=newg.dist(ida,idb)+ori.dis[aa]-ori.dis[roota]+ori.dis[bb]-ori.dis[rootb];
if(ida==lca){
int frb=from[newg.up(idb,lca)];
res-=ori.dis[aa]+ori.dis[frb]-ori.dist(aa,frb)-2*ori.dis[roota];
}
else if(idb==lca){
int fra=from[newg.up(ida,lca)];
res-=ori.dis[bb]+ori.dis[fra]-ori.dist(bb,fra)-2*ori.dis[rootb];
}
else{
int fra=from[newg.up(ida,lca)];
int frb=from[newg.up(idb,lca)];
res-=ori.dis[fra]+ori.dis[frb]-ori.dist(fra,frb)-2*ori.dis[root[lca]];
}
printf("%lld\n",res);
}
int main(){
ori.init();
newg.init();
int i,j,k;
cin>>n>>m>>q;
for(i=1;i