bzoj4539【HNOI2016】树

来源:互联网 发布:淘宝悬浮导航分类代码 编辑:程序博客网 时间:2024/06/07 21:14

4539: [Hnoi2016]树

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 415  Solved: 157
[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之间的距离是多少。

Output

  输出Q行,每行一个整数,第 i行是第 i个询问的答案。

Sample Input

5 2 3
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3

Sample Output

6
3
3

HINT

经过两次操作后,大树变成了下图所示的形状:



结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。






树分块+可持久化线段树,思路好题

新树的节点较多,直接表示比较麻烦,我们考虑简化树的形态。

将每次操作的一棵子树看成一块,每一块用根节点表示,可以得到一棵新树,即新树中一个节点代表一块。

对于一次询问,如果两个点在同一块内,则直接在原树中求LCA计算答案。如果不在同一块中,要分两块在新树中是否是父子关系两种情况,然后就是比较细节的问题了。

还有一个问题,新树中的节点编号如何对应原树中的节点编号。首先二分出这个节点属于第几块,然后转化成求一个子树中编号第k大的点,对于原树的DFS序建可持久化线段树。

实现起来很麻烦,各种各样的情况,足足调了一下午。




#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define F(i,j,n) for(int i=j;i<=n;i++)#define D(i,j,n) for(int i=j;i>=n;i--)#define ll long long#define N 100005#define M 2000000using namespace std;int n,m,q,tim,now;int dfn[N],last[N],root[N],from[N];ll num[N];struct edge{int next,to;ll v;};struct Segment{int cnt,sz[M],ch[M][2],rt[N];void insert(int x,int &y,int l,int r,int pos){y=++cnt;sz[y]=sz[x]+1;if (l==r) return;int mid=(l+r)>>1;if (pos<=mid) ch[y][1]=ch[x][1],insert(ch[x][0],ch[y][0],l,mid,pos);else ch[y][0]=ch[x][0],insert(ch[x][1],ch[y][1],mid+1,r,pos);}void insert(int x,int v){insert(rt[x-1],rt[x],1,n,v);}int query(int x,int y,int l,int r,int k){if (l==r) return l;int mid=(l+r)>>1,tmp=sz[ch[y][0]]-sz[ch[x][0]];if (tmp>=k) return query(ch[x][0],ch[y][0],l,mid,k);else return query(ch[x][1],ch[y][1],mid+1,r,k-tmp);}int query(int x,int y,int k){return query(rt[x-1],rt[y],1,n,k);}}T;struct Graph{edge e[N*2];int cnt,head[N],fa[N][20],dep[N],sz[N];ll dis[N];void add_edge(int x,int y,ll v){e[++cnt]=(edge){head[x],y,v};head[x]=cnt;e[++cnt]=(edge){head[y],x,v};head[y]=cnt;}void dfs(int x){F(i,1,18) fa[x][i]=fa[fa[x][i-1]][i-1];sz[x]=1;for(int i=head[x];i;i=e[i].next){int y=e[i].to;if (y!=fa[x][0]){fa[y][0]=x;dis[y]=dis[x]+e[i].v;dep[y]=dep[x]+1;dfs(y);sz[x]+=sz[y];}}}void dfs2(int x){dfn[x]=++tim;T.insert(tim,x);for(int i=head[x];i;i=e[i].next){int y=e[i].to;if (y!=fa[x][0]) dfs2(y);}last[x]=tim;}int lca(int x,int y){if (dep[x]<dep[y]) swap(x,y);int tmp=dep[x]-dep[y];D(i,18,0) if ((1<<i)&tmp) x=fa[x][i];if (x==y) return x;D(i,18,0) 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]-dis[lca(x,y)]*2;}int up(int x,int y){int tmp=dep[x]-dep[y]-1;D(i,18,0) if ((1<<i)&tmp) x=fa[x][i];return x;}}ori,g;inline int read(){int x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}inline int getid(ll x,int len){return lower_bound(num+1,num+len+1,x)-num;}ll query(ll a,ll b){int ida=getid(a,m+1),rta=root[ida],posa=T.query(dfn[rta],last[rta],a-num[ida-1]);int idb=getid(b,m+1),rtb=root[idb],posb=T.query(dfn[rtb],last[rtb],b-num[idb-1]);if (ida==idb) return ori.dist(posa,posb);int lca=g.lca(ida,idb);if (g.dep[ida]>g.dep[idb]) swap(ida,idb),swap(rta,rtb),swap(posa,posb);if (ida==lca){int frb=from[g.up(idb,lca)];return g.dist(ida,idb)-(ori.dis[frb]-ori.dis[rta])+ori.dist(frb,posa)+ori.dis[posb]-ori.dis[rtb];}else{int fra=from[g.up(ida,lca)],frb=from[g.up(idb,lca)];return g.dist(ida,idb)-(ori.dis[fra]+ori.dis[frb]-ori.dist(fra,frb)-ori.dis[root[lca]]*2)+ori.dis[posa]-ori.dis[rta]+ori.dis[posb]-ori.dis[rtb];}}int main(){n=read();m=read();q=read();F(i,1,n-1){int x=read(),y=read();ori.add_edge(x,y,1);}ori.dfs(1);ori.dfs2(1);num[1]=n;root[1]=1;now=1;F(i,2,m+1){ll x,y;scanf("%lld%lld",&x,&y);int id=getid(y,i-1),rt=root[id];root[i]=x;now=i;num[i]=num[i-1]+ori.sz[x];from[i]=T.query(dfn[rt],last[rt],y-num[id-1]);g.add_edge(id,i,ori.dis[from[i]]-ori.dis[rt]+1);}g.dfs(1);F(i,1,q){ll x,y;scanf("%lld%lld",&x,&y);printf("%lld\n",query(x,y));}return 0;}


树分块+可持久化线段树,思路好题
0 0
原创粉丝点击