【BZOJ 4448】 [Scoi2015]情报传递|倍增LCA|主席树

树套树在这

不少技巧没有学过 %yts 的代码

果然离线主席树 比 我的SB树套树好写多了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN=200000+10;
int fa[MAXN],depth[MAXN],to[MAXN][19];//18
int team[MAXN],head,tail;
int tot,g[MAXN],nnext[MAXN],num[MAXN];
int mn[MAXN];
int n,m;
int root[MAXN],rtc;
struct A{int x,y,z;}init[MAXN];
struct B{int l,r,sum;}seg[MAXN*20];
void add(int x,int y)
{
	tot++;
	nnext[tot]=g[x];
	g[x]=tot;
	num[tot]=y;
}
void get_lca(int rt)
{
	team[++tail]=rt;
	depth[rt]=1;
	while(head!=tail)
	{
		int x=team[++head];
		to[x][0]=fa[x];
		for(int i=1;i<=18;i++)
			to[x][i]=to[to[x][i-1]][i-1];
		for(int i=g[x];i;i=nnext[i])
		{
			depth[num[i]]=depth[x]+1;
			fa[num[i]]=x;
			team[++tail]=num[i];
		}
	}
}
int lca(int x,int y)
{
	if(depth[x]<depth[y]) swap(x,y);
	for(int i=18;i>=0;i--)
		if(depth[to[x][i]]>=depth[y])
			x=to[x][i];
	if(x==y) return x;
	
	for(int i=18;i>=0;i--)
		if(to[x][i]!=to[y][i])
			x=to[x][i],y=to[y][i];
	return to[x][0];
}
void build(int frt,int &rt,int l,int r,int x)
{
	rt=++rtc;
	seg[rt].sum++;
	if(l==r) return ;
	int mid=(l+r)/2;
	if(x<=mid) seg[rt].r=seg[frt].r,build(seg[frt].l,seg[rt].l,l,mid,x);
	else seg[rt].l=seg[frt].l,build(seg[frt].r,seg[rt].r,mid+1,r,x);
	seg[rt].sum=seg[seg[rt].l].sum+seg[seg[rt].r].sum;
}
int q(int rt,int l,int r,int x)
{
	if(rt==0) return 0;
	if(r<=x) return seg[rt].sum;
	int mid=(l+r)/2;
	if(x<=mid) return q(seg[rt].l,l,mid,x);
	else return q(seg[rt].l,l,mid,x)+q(seg[rt].r,mid+1,r,x);
}
int read()
{
	char c=getchar();int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9'&&!feof(stdin)) x=x*10+c-'0',c=getchar();
	return x;
}
int main()
{
	int rt,x,y,z,opt,cnt=0;
	n=read();
	for(int i=1;i<=n;i++)
	{
		x=read();
		if(x==0) rt=i;
		else add(x,i);
	}
	get_lca(rt);
	m=read();
	for(int i=1;i<=n;i++) mn[i]=m+1;
	for(int i=1;i<=m;i++)
	{
		opt=read();
		if(opt==1)
			init[++cnt]=(A){read(),read(),i-read()-1};
		else
		{
			x=read();
			mn[x]=min(mn[x],i);
		}
	}
	for(int i=1;i<=n;i++)
	{
		x=team[i];
		y=fa[x];
		build(root[y],root[x],1,n,mn[x]);
	}
	for(int i=1;i<=cnt;i++)
	{
		x=init[i].x;
		y=init[i].y;
		z=init[i].z;
		int f=lca(x,y);
		printf("%d %d\n",
			depth[x]+depth[y]-depth[f]*2+1,
			q(root[x],1,n,z)+
			q(root[y],1,n,z)-
			q(root[fa[f]],1,n,z)*2-
			(mn[f]<=z?1:0)
			);	
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值