bzoj4539HNOI2016树

Posted AaronPolaris

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4539HNOI2016树相关的知识,希望对你有一定的参考价值。

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 2000000
using 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;
}


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









































以上是关于bzoj4539HNOI2016树的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4539 [Hnoi2016]树

bzoj 4539: [Hnoi2016]树

BZOJ 4541: [Hnoi2016]矿区 平面图转对偶图+DFS树

BZOJ4538: [Hnoi2016]网络

BZOJ 4538 [Hnoi2016]网络

●BZOJ 4541 [Hnoi2016]矿区