BZOJ 4539: [Hnoi2016]树

md强行凑数据结构题,树剖+主席树,各种函数搞一大堆查询,然后其实就是求两点的lca。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100000+5;
#define rep(i,l,r) for(int i=l;i<=r;i++)
typedef long long ll;
ll read(){
	char ch=getchar();ll x=0,f=1;
	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;
}
struct Edge{int to,next;}e[N<<1],g[N<<1];
int head[N],cnt,last[N],tot;
void ins1(int u,int v){e[++cnt]=(Edge){v,head[u]};head[u]=cnt;}
void insert1(int u,int v){ins1(u,v);ins1(v,u);}
void ins2(int u,int v){g[++tot]=(Edge){v,last[u]};last[u]=tot;}
void insert2(int u,int v){ins2(u,v);ins2(v,u);}
int fa1[N],dep1[N],top1[N],son1[N],siz1[N],st[N],ed[N],dfn[N],sz;
void dfs1(int u){
	dfn[st[u]=++sz]=u;siz1[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;if(v==fa1[u])continue;
		fa1[v]=u;dep1[v]=dep1[u]+1;dfs1(v);
		siz1[u]+=siz1[v];if(siz1[v]>siz1[son1[u]])son1[u]=v;
	}
	ed[u]=sz;
}
void dfs1(int u,int tp){
	top1[u]=tp;
	if(son1[u])dfs1(son1[u],tp);
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v!=fa1[u]&&v!=son1[u])dfs1(v,v);
	}
}
struct Node{
	int lc,rc,w;
}tr[N*20];
int node,root[N];
void build(int &o,int l,int r){
	o=++node;
	if(l==r)return;
	int mid=l+r>>1;
	build(tr[o].lc,l,mid);build(tr[o].rc,mid+1,r);
}
void insert(int &o,int l,int r,int p){
	tr[++node]=tr[o];o=node;
	tr[o].w++;
	if(l==r)return;
	int mid=l+r>>1;
	if(p<=mid)insert(tr[o].lc,l,mid,p);
	else insert(tr[o].rc,mid+1,r,p);
}
int query(int x,int y,int l,int r,int k){
	if(l==r)return l;
	int mid=l+r>>1;
	int sum=tr[tr[y].lc].w-tr[tr[x].lc].w;
	if(k<=sum)
	return query(tr[x].lc,tr[y].lc,l,mid,k);
	else return query(tr[x].rc,tr[y].rc,mid+1,r,k-sum);
}
int rank(int x,int y,int l,int r,int k){
	if(l==r)return tr[y].w-tr[x].w;
	int mid=l+r>>1;
	if(k<=mid)return rank(tr[x].lc,tr[y].lc,l,mid,k);
	else return rank(tr[x].rc,tr[y].rc,mid+1,r,k)+tr[tr[y].lc].w-tr[tr[x].lc].w;
}
int query(int u,int k){
	return query(root[st[u]-1],root[ed[u]],1,sz,k);
}
int rank(int u,int k){
	return rank(root[st[u]-1],root[ed[u]],1,sz,k);
}
int fa2[N],dep2[N],siz2[N],son2[N],top2[N],cost[N];
ll depth[N];
void dfs2(int u){
	siz2[u]=1;
	for(int i=last[u];i;i=g[i].next){
		int v=g[i].to;if(v==fa2[u])continue;
		dep2[v]=dep2[u]+1;fa2[v]=u;depth[v]=depth[u]+cost[v];dfs2(v);
		siz2[u]+=siz2[v];if(siz2[v]>siz2[son2[u]])son2[u]=v;
	}
}
void dfs2(int u,int tp){
	top2[u]=tp;
	if(son2[u])dfs2(son2[u],tp);
	for(int i=last[u];i;i=g[i].next){
		int v=g[i].to;
		if(v!=son2[u]&&v!=fa2[u])dfs2(v,v);
	}
}
ll size[N];
int n,m,now;
int belong(ll id){
	int l=1,r=now;
	while(l<=r){
		int mid=l+r>>1;
		if(size[mid]<id)l=mid+1;
		else r=mid-1;
	}
	return l-1;
}
int lca1(int u,int v){
	while(top1[u]!=top1[v]){
		if(dep1[top1[u]]<dep1[top1[v]])swap(u,v);
		u=fa1[top1[u]];
	}
	return dep1[u]<dep1[v]?u:v;
}
ll li[N];
int rt[N];
ll lca2(ll u,ll v){
	int x=belong(u),y=belong(v);
	while(top2[x]!=top2[y]){
		if(dep2[top2[x]]<dep2[top2[y]])swap(x,y),swap(u,v);
		x=top2[x];u=li[x];x=fa2[x];
	}
	if(x!=y){
		if(dep2[x]>dep2[y])swap(x,y),swap(u,v);
		y=x;v=li[son2[y]];
	}
	return size[x]+rank(rt[x],lca1(query(rt[x],u-size[x]),query(rt[y],v-size[y])));
}
ll dep(ll u){
	int x=belong(u);
	return depth[x]+dep1[query(rt[x],u-size[x])]-dep1[rt[x]];
}
ll dist(ll u,ll v){
	return dep(u)+dep(v)-2*dep(lca2(u,v));
}
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int q;n=read();m=read();q=read();
	rep(i,2,n){int u,v;u=read();v=read();insert1(u,v);}
	dfs1(1);dfs1(1,1);
	build(root[0],1,sz);
	rep(i,1,sz){
		root[i]=root[i-1];
		insert(root[i],1,sz,dfn[i]);
	}
	rt[1]=1;m++;now=1;
	rep(i,2,m){
		rt[i]=read();li[i]=read();size[i]=size[i-1]+siz1[rt[i-1]];
		int x=belong(li[i]);
		insert2(i,x);
		cost[i]=dep1[query(rt[x],li[i]-size[x])]-dep1[rt[x]]+1;
		now++;
	}
	dfs2(1);dfs2(1,1);
	while(q--){
		ll u,v;u=read();v=read();
		printf("%lld\n",dist(u,v));
	}
	return 0;
}
	
	


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值