loj #2558. 「LNOI2014」LCA & #3088. 「GXOI / GZOI2019」旧词

7 篇文章 0 订阅
4 篇文章 0 订阅

背景:

这都是套路题?
见的题太少了(貌似在模拟赛见过???)。

题目传送门:

https://loj.ac/problem/2558
https://loj.ac/problem/3088

题意:

P 2588 P2588 P2588:一棵树,多组询问,给出 x , y , z x,y,z x,y,z,求 ∑ i = x y d e p [ lca i , z ] \sum_{i=x}^{y}dep[\text{lca}_{i,z}] i=xydep[lcai,z]
P 3088 P3088 P3088:一棵树,多组询问,给出 x , y x,y x,y,求 ∑ i = 1 x d e p [ lca i , y ] z \sum_{i=1}^{x}dep[\text{lca}_{i,y}]^z i=1xdep[lcai,y]z

思路:

其实两个题差不多。
你要解决的问题都是形如 ∑ i l c a ( i , x ) \sum_ilca(i,x) ilca(i,x)的问题。
方法就是从 i ∈ [ x , y ] i∈[x,y] i[x,y]的从 i i i 1 1 1路径全部 + 1 +1 +1,统计答案时就是从 1 1 1(根节点)到 z z z的路径的权值和。
为什么是对的呢?
d e p [ l c a ] dep[lca] dep[lca]的定义是 l c a lca lca的深度为它到根结点的距离 + 1 +1 +1,那么从一个结点出发,给它到根结点的链上的节点累加1的时候,显然是从 l c a lca lca开始才会对询问点产生贡献的。而这个贡献个就是从 l c a lca lca开始,到根结点(包含根节点)的点数,而这正等于题目中对 l c a lca lca深度的定义。

由于 P 2588 P2588 P2588求的是 ∑ x y \sum_{x}^{y} xy的贡献,等同于 ∑ 1 y − ∑ 1 x − 1 \sum_1^y-\sum_1^{x-1} 1y1x1。实现方法就是离线按 x − 1 , y x-1,y x1,y的值升序,用树剖维护(可以差分,也可以不用,我当然~~~不用了)。

P 3088 P3088 P3088有一个 z z z次方,怎么办呢?
考虑

P 2558 P2558 P2558代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 201314
using namespace std;
	int n,m,q,u=0,len=0,op=0;
	struct node1{int x,y,next;} a[200010];
	struct node2{int d,l,r,lc,rc,lazy,n;} tr[200010];
	struct node3{int x,y,id,t;} b[200010];
	int last[200010],p[200010],tot[200010],son[200010],fa[200010],dep[200010],ys[200010],yss[200010],top[200010],ans[200010];

void ins(int x,int y)
{
	a[++len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;
}
void build(int l,int r)
{
	int now=++len;
	tr[now]=(node2){0,l,r,-1,-1,0,r-l+1};
	if(l<r)
	{
		int mid=(l+r)>>1;
		tr[now].lc=len+1; build(l,mid);
		tr[now].rc=len+1; build(mid+1,r);
	}
}
void update(int now)
{
	if(tr[now].lazy)
	{
		int lc=tr[now].lc,rc=tr[now].rc;
		if(lc!=-1) tr[lc].d+=tr[lc].n*tr[now].lazy,tr[lc].lazy+=tr[now].lazy;
		if(rc!=-1) tr[rc].d+=tr[rc].n*tr[now].lazy,tr[rc].lazy+=tr[now].lazy;
		tr[now].lazy=0;
	}
}
void change(int now,int l,int r,int k)
{
	update(now);
	if(tr[now].l==l&&tr[now].r==r)
	{
		tr[now].d+=tr[now].n*k;
		tr[now].lazy+=k;
		return;
	}
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(mid+1<=l) change(rc,l,r,k);
	else if(r<=mid) change(lc,l,r,k);
	else change(lc,l,mid,k),change(rc,mid+1,r,k);
	tr[now].d=tr[lc].d+tr[rc].d;
}
int findsum(int now,int l,int r)
{
	update(now);
	if(tr[now].l==l&&tr[now].r==r) return tr[now].d;
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(mid+1<=l) return findsum(rc,l,r);
	else if(r<=mid) return findsum(lc,l,r);
	else return findsum(lc,l,mid)+findsum(rc,mid+1,r);
}
void dfs1(int x)
{
	tot[x]=1;
	son[x]=0;
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y!=fa[x])
		{
			fa[y]=x;
			dep[y]=dep[x]+1;
			dfs1(y);
			if(tot[son[x]]<tot[y]) son[x]=y;
			tot[x]+=tot[y];
		}
	}
}
void dfs2(int x,int tp)
{
	ys[x]=++u;
	top[x]=tp;
	if(son[x]) dfs2(son[x],tp);
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y!=son[x]&&y!=fa[x]) dfs2(y,y);
	}
}
void add(int x,int y)
{
	int tx=top[x],ty=top[y];
	while(tx!=ty)
	{
		if(dep[tx]>dep[ty]) swap(x,y),swap(tx,ty);
		change(1,ys[ty],ys[y],1);
		y=fa[ty];ty=top[y];
	}
	if(dep[x]>dep[y]) swap(x,y);
	change(1,ys[x],ys[y],1);
}
int solve(int x,int y)
{
	int tx=top[x],ty=top[y],ans=0;
	while(tx!=ty)
	{
		if(dep[tx]>dep[ty]) swap(x,y),swap(tx,ty);
		ans+=findsum(1,ys[ty],ys[y]);
		y=fa[ty];ty=top[y];
	}
	if(dep[x]>dep[y]) swap(x,y);
	return ans+findsum(1,ys[x],ys[y]);
}
bool cmp(node3 x,node3 y)
{
	return x.x<y.x;
}
int main()
{
	int x,y,z;
	scanf("%d %d",&n,&m);
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&x);
		x++;
		ins(x,i),ins(i,x);
	}
	len=0;
	build(1,n);
	fa[1]=0;
	dep[1]=1;
	dfs1(1);
	dfs2(1,0);
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		x++,y++,z++;
		b[++op]=(node3){x-1,z,i,-1};
		b[++op]=(node3){y,z,i,1};
	}
	sort(b+1,b+op+1,cmp);
	int now=0;
	for(int i=1;i<=op;i++)
	{
		while(now<b[i].x)
		{
			now++;
			add(1,now);
		}
		ans[b[i].id]+=b[i].t*solve(1,b[i].y);
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]%mod);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值