BZOJ 3626: [LNOI2014]LCA

10 篇文章 0 订阅
2 篇文章 0 订阅
帮助复习了树剖的基本操作。
需要考虑一下lca最近公共祖先的最原始的性质,这样就可以来用树剖加线段树维护了。
但是这样是个傻逼复杂度:O(n∗logn∗logn*q)(树剖加线段树,两个log),我们需要把q去掉,所以就离线做一遍,利用前缀和,差分一下即可。和gss2的离线思路差不多类似,可以算作一种套路吧。
吐槽:
一个晚上就做了一道题???我还是太菜了呀…
明明有时间的,但是做完一题整个人就痿了???…
#include <bits/stdc++.h>
using namespace std;
const int N=5e4+5,MOD=201314;
int n,m,u,l,r,z;
int size[N],d[N],f[N],son[N];
int tot,top[N],id[N];
int cnt,head[N];
struct edge{int from,next,to;}e[N<<1];
int sum[N<<2],tag[N<<2];
int summ,ans1[N],ans2[N];
struct number{int pos,s,id,f;}num[N<<1]; 

inline void add(int u,int v)
{
	cnt++;
	e[cnt].next=head[u];
	e[cnt].from=u;
	e[cnt].to=v;
	head[u]=cnt;
}

void dfs(int u,int fa)
{
	size[u]=1;
	for (register int i=head[u]; i; i=e[i].next)
	if (e[i].to!=fa)
	{
		d[e[i].to]=d[u]+1;
		f[e[i].to]=u;
		dfs(e[i].to,u);
		size[u]+=size[e[i].to];
		if (size[son[u]]<size[e[i].to]) son[u]=e[i].to;	
	}
}

void dfs2(int u,int TP)
{
	tot++;
	id[u]=tot;
	top[u]=TP;
	if (son[u]) dfs2(son[u],TP);
	for (register int i=head[u]; i; i=e[i].next) 
	if (e[i].to!=f[u] && e[i].to!=son[u]) dfs2(e[i].to,e[i].to);
}

inline void pushdown(int k,int l,int r)
{
	int mid=l+r>>1;
	if (!tag[k]) return;
	tag[k<<1]+=tag[k]; tag[k<<1|1]+=tag[k];
	tag[k<<1]%=MOD; tag[k<<1|1]%=MOD;
	sum[k<<1]+=(mid-l+1)*tag[k];
	sum[k<<1|1]+=(r-(mid+1)+1)*tag[k];
	sum[k<<1]%=MOD; sum[k<<1|1]%=MOD; 
	tag[k]=0;
}

void change(int k,int l,int r,int qx,int qy,int v)
{
	if (qx<=l && r<=qy)
	{
		sum[k]+=(r-l+1)*v;
		sum[k]%=MOD;
		tag[k]+=v;
		tag[k]%=MOD;
		return;
	}
	pushdown(k,l,r);
	int mid=l+r>>1;
	if (qx<=mid) change(k<<1,l,mid,qx,qy,v);
	if (mid<qy) change(k<<1|1,mid+1,r,qx,qy,v);
	sum[k]=sum[k<<1]+sum[k<<1|1]; 
	sum[k]%=MOD;
}

int query(int k,int l,int r,int qx,int qy)
{
	if (qx<=l && r<=qy) return sum[k];
	pushdown(k,l,r);
	int mid=l+r>>1;
	int ans=0;
	if (qx<=mid) ans=(ans+query(k<<1,l,mid,qx,qy));
	if (mid<qy) ans=(ans+query(k<<1|1,mid+1,r,qx,qy));
	return ans; 
}

inline void modify(int u,int v,int z)
{
	while (top[u]!=top[v])
	{
		if (d[top[u]]<d[top[v]]) swap(u,v);
		change(1,1,n,id[top[u]],id[u],z);
		u=f[top[u]];
	}
	if (d[u]>d[v]) swap(u,v);
	change(1,1,n,id[u],id[v],z);
}

inline int qsum(int u,int v)
{
	int ans=0;
	while (top[u]!=top[v])
	{
		if (d[top[u]]<d[top[v]]) swap(u,v);
		ans=(ans+query(1,1,n,id[top[u]],id[u]))%MOD;
		u=f[top[u]];
	}
	if (d[u]>d[v]) swap(u,v);
	ans=(ans+query(1,1,n,id[u],id[v]))%MOD;
	return ans;
}

inline bool cmp(number a,number b)
{
	return a.pos<b.pos;
}

int main(){
	scanf("%d%d",&n,&m);
	for (register int i=2; i<=n; ++i)
	{
		scanf("%d",&u); u++;
		add(u,i); add(i,u);
	}
	
	d[1]=1; f[1]=0;
	dfs(1,0); dfs2(1,1);
	
	for (register int i=1; i<=m; ++i)
	{
		scanf("%d%d%d",&l,&r,&z);
		l++; r++; z++;
		summ++;
		num[summ].pos=l-1; num[summ].s=z; num[summ].id=i; num[summ].f=0;
		summ++;
		num[summ].pos=r; num[summ].s=z; num[summ].id=i; num[summ].f=1;
	}
	sort(num+1,num+summ+1,cmp);
	
	int now=0;
	for (register int i=1; i<=summ; ++i)
	{
		while (now<num[i].pos)
		{
			now++;
			modify(1,now,1);
		}
		if (!num[i].f) ans1[num[i].id]=qsum(1,num[i].s);
		else ans2[num[i].id]=qsum(1,num[i].s);
	}
	
	for (register int i=1; i<=m; ++i) printf("%d\n",(ans2[i]-ans1[i]+MOD)%MOD);
return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值