BZOJ 3626 [LNOI2014]LCA - 树链剖分+线段树结构+玄学离散

考察内容:树链剖分+线段树


题意:给定一棵树,对于每一个l,r,z,求Σ(l<=i<=r)depth[lca(i,z)]


规模:n,q<=50000

分析:
1.首先考虑到倍增lca,很明显时间复杂度不允许其次考虑离线Tarjan,时间复杂度可以满足,然而会MLE
2.看到所求的值存在巨大的重复之处,考虑到莫队算法, 然而不会,根号复杂度不允许,所以进行玄学离散,利用一棵查询树进行查询
3.方法只剩下了lct O(nlogn)和重链剖分O(n*log^2) ,然而lc不会。现在思考怎样利用树链剖分完成查询任务查询任务为一个区间,可以利用差分,求sum[r]-sum[l-1]的值。可持久化线段树,一下想到主席树,然而无法完成任务,于是进行玄学离散,将每一个ans拆成两部分,即加sum[r]和减sum[l-1],拆成两个任务来查询

4.a和b的lca深度=根节点到a的路径染色,根节点到b的路径上染色数 现在进行更新,边更新边查询,标记每个点到根的路径,路径上每个点+1,然后更新完毕查询当前时间的depth[lca],然后继续标记,结束后输出ans数组即可。


//a和b的lca深度=
//先把根节点到a的路径都染色,然后查根节点到b的路径上染色点数
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#define mod 201314

using namespace std;

const int maxn=50005;

struct seg_tree
{
	int lt,rt,val,add;
}t[maxn<<2];

struct question
{
	int fin,point,id,d;
}q[maxn<<1];

struct edge
{
	int to,next;
}e[maxn];

int cnt,qnum,n,m,cnt_clock;
int head[maxn],ans[maxn];
int size[maxn],son[maxn],top[maxn],id[maxn],pere[maxn];

bool cmp(question a,question b)
{
	return a.fin<b.fin;
}
void insert(int a,int b)
{
	e[++cnt].to=b;e[cnt].next=head[a];head[a]=cnt;
}
void build(int ro,int l,int r)
{
	t[ro].lt=l;
	t[ro].rt=r;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(ro<<1,l,mid);
	build(ro<<1|1,mid+1,r);
}
inline int getnum(int ro)
{
	return t[ro].rt-t[ro].lt+1;
}
inline void maintain(int ro)
{
	t[ro].val=(t[ro<<1].val+t[ro<<1|1].val)%mod;
}
void pushdown(int ro)
{
	if(t[ro].add&&(t[ro].lt!=t[ro].rt))
	{
		t[ro<<1].add+=t[ro].add;
		t[ro<<1|1].add+=t[ro].add;
		t[ro<<1].add%=mod;
		t[ro<<1|1].add%=mod;
		t[ro<<1].val+=t[ro].add*getnum(ro<<1);
		t[ro<<1|1].val+=t[ro].add*getnum(ro<<1|1);
		t[ro<<1].val%=mod;
		t[ro<<1|1].val%=mod;
		t[ro].add=0;
	}
}
void add(int ro,int l,int r,int val)
{
	if(t[ro].lt==l&&t[ro].rt==r)
	{
		t[ro].add+=val;
		t[ro].val+=val*getnum(ro);//线段树写挂了233 
		t[ro].val%=mod;
		return;
	}
	pushdown(ro);
	int mid=(t[ro].lt+t[ro].rt)>>1;
	if(r<=mid)add(ro<<1,l,r,val);
	else if(l>=mid+1)add(ro<<1|1,l,r,val);
	else add(ro<<1,l,mid,val),add(ro<<1|1,mid+1,r,val);
	maintain(ro);
}
int query(int ro,int l,int r)
{
	if(t[ro].lt==l&&t[ro].rt==r)return t[ro].val;
	pushdown(ro);
	int mid=(t[ro].lt+t[ro].rt)>>1;
	if(r<=mid)return query(ro<<1,l,r);
	else if(l>=mid+1)return query(ro<<1|1,l,r);
	else return (query(ro<<1,l,mid)+query(ro<<1|1,mid+1,r))%mod;
}
void dfs_getsz(int x)
{
	size[x]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		dfs_getsz(e[i].to);
		size[x]+=size[e[i].to];
		if(size[e[i].to]>size[son[x]])son[x]=e[i].to;
	}
}
void dfs_chain(int x,int tp)
{
	top[x]=tp;
	id[x]=++cnt_clock;
	if(son[x])
		dfs_chain(son[x],tp);
	for(int i=head[x];i;i=e[i].next)
	{
		if(e[i].to==son[x])continue;
		dfs_chain(e[i].to,e[i].to);
	}
}
void chain_add(int x)
{
	int tp=top[x];
	while(x)
	{
		add(1,id[tp],id[x],1);
		x=pere[tp];tp=top[x];
	}
}
int chain_query(int now)
{
	int res=0;
	int x=q[now].point,tp=top[x];
	while(x)
	{
		res+=query(1,id[tp],id[x]);
		res%=mod;
		x=pere[tp];tp=top[x];
	}
	return res;
}
void make_seg_tree()
{
	int now=1;
	for(int i=0;i<=n;i++)
	{
		chain_add(i);
		for(;i==q[now].fin;now++)
			ans[q[now].id]+=chain_query(now)*q[now].d;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++)
	{
		int a;
		scanf("%d",&a);
		pere[i]=a+1;
		insert(a+1,i);
	}
	dfs_getsz(1);
	dfs_chain(1,1);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&q[++qnum].fin);
		q[qnum].d=-1;
		scanf("%d",&q[++qnum].fin);
		q[qnum].fin++;
		q[qnum].d=1;
		int a;
		scanf("%d",&a);
		q[qnum].point=q[qnum-1].point=a+1;
		q[qnum].id=q[qnum-1].id=i;
	}
	sort(q+1,q+qnum+1,cmp);
	build(1,1,n);
	make_seg_tree();
	for(int i=1;i<=m;i++)
		printf("%d\n",(ans[i]+mod)%mod);
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值