bzoj3626【LNOI2014】LCA

3626: [LNOI2014]LCA

Time Limit: 10 Sec  Memory Limit: 128 MB

Submit: 1266  Solved: 448

[Submit][Status][Discuss]

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。

设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。

有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。

(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

Input

第一行2个整数n q。

接下来n-1行,分别表示点1到点n-1的父节点编号。

接下来q行,每行3个整数l r z。

Output

输出q行,每行表示一个询问的答案。每个答案对201314取模输出

Sample Input

5 2

0

0

1

1

1 4 3

1 4 2

Sample Output

8

5

HINT

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

Source

数据已加强 by saffah

这道题思路很好!

如果把x到根的权值全部加1,那么y到根的权值和就增加dep[lca(x,y)]。

扩展到区间,如果把[l,r]的点到根的权值全部加1,那么z到根的权值和就增加∑(l≤i≤r)dep[lca(i,z)]。那么对于每一个(l,r,z)的询问,我们就可以拆成两个前缀和来离线处理了。

链查询,树链剖分+线段树。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 50005
#define mod 201314
using namespace std;
struct edge{int next,to;}e[maxn];
struct seg{int l,r,sum,tag;}t[maxn*4];
struct data{int next,z,pos,tag;}g[maxn*2];
int n,m,cnt,tot;
int p[maxn],sz[maxn],fa[maxn],son[maxn],ans[maxn];
int head[maxn],belong[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	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;
}
inline void add_edge(int x,int y)
{
	e[++cnt]=(edge){head[x],y};head[x]=cnt;
}
inline void add_data(int x,int y,int num,int tg)
{
	g[++cnt]=(data){head[x],y,num,tg};head[x]=cnt;
}
inline void dfs1(int x)
{
	sz[x]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].to;
		fa[y]=x;
		dfs1(y);
		sz[x]+=sz[y];
		if (sz[y]>sz[son[x]]) son[x]=y;
	}
}
inline void dfs2(int x,int chain)
{
	belong[x]=chain;p[x]=++tot;
	if (son[x]) dfs2(son[x],chain);
	for(int i=head[x];i;i=e[i].next)
		if (e[i].to!=son[x]) dfs2(e[i].to,e[i].to);
}
inline void update(int k,int z)
{
	t[k].sum+=z*(t[k].r-t[k].l+1);
	t[k].tag+=z;
}
inline void pushdown(int k)
{
	if (!t[k].tag) return;
	update(k<<1,t[k].tag);update(k<<1|1,t[k].tag);
	t[k].tag=0;
}
inline void pushup(int k)
{
	t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}
inline void build(int k,int l,int r)
{
	t[k].l=l;t[k].r=r;t[k].sum=0;
	if (l==r) return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
inline int query(int k,int x,int y)
{
	if (t[k].l==x&&t[k].r==y) return t[k].sum;
	int mid=(t[k].l+t[k].r)>>1;
	pushdown(k);
	if (y<=mid) return query(k<<1,x,y);
	else if (x>mid) return query(k<<1|1,x,y);
	else return query(k<<1,x,mid)+query(k<<1|1,mid+1,y);
}
inline void add(int k,int x,int y,int z)
{
	if (t[k].l==x&&t[k].r==y){update(k,z);return;}
	int mid=(t[k].l+t[k].r)>>1;
	pushdown(k);
	if (y<=mid) add(k<<1,x,y,z);
	else if (x>mid) add(k<<1|1,x,y,z);
	else add(k<<1,x,mid,z),add(k<<1|1,mid+1,y,z);
	pushup(k);
}
inline void solveadd(int x)
{
	while (belong[x]!=1)
	{
		add(1,p[belong[x]],p[x],1);
		x=fa[belong[x]];
	}
	add(1,p[1],p[x],1);
}
inline int solvesum(int x)
{
	int sum=0;
	while (belong[x]!=1)
	{
		sum+=query(1,p[belong[x]],p[x]);
		x=fa[belong[x]];
	}
	sum+=query(1,p[1],p[x]);
	return sum;
}
int main()
{
	n=read();m=read();
	F(i,2,n) add_edge(read()+1,i);
	dfs1(1);dfs2(1,1);
	build(1,1,n);
	cnt=tot=0;
	memset(head,0,sizeof(head));
	F(i,1,m)
	{
		int l=read()+1,r=read()+1,z=read()+1;
		add_data(l-1,z,i,-1);add_data(r,z,i,1);
	}
	F(i,1,n)
	{
		solveadd(i);
		for(int j=head[i];j;j=g[j].next)
			ans[g[j].pos]+=solvesum(g[j].z)*g[j].tag;
	}
	F(i,1,m) printf("%d\n",ans[i]%mod);
}
时间: 2024-10-03 01:16:32

bzoj3626【LNOI2014】LCA的相关文章

【BZOJ3626】【LNOI2014】LCA (树剖+离线)

Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)].(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)答案对201314取模. 这个题是大饺子安利给我的,然后顺带学了一发树剖(好弱啊). 这个题解讲的很好啦w:http://www.cnbl

【总结】LCA的4种求法

前言 LCA的求法有多重多样,总结下来是下面这4种.希望大家可以加油! 暴力求LCA 我们考虑dfs求出每一个点的父亲(在当前根下),然后直接先暴力跳到同一个深度,再同时跳 void dfs(int u,int f){ fa[u]=f;dep[u]=dep[f]+1; for(re int i=front[u];i;i=e[i].nxt){ int v=e[i].to; if(v==f)continue; dfs(v,u); } } int lca(int u,int v){ if(dep[u]

【模板】LCA

(十一集训前最后的挣扎) 先介绍LCA是啥吧.. LCA:Lowest Common Ancestors(最近公共祖先) 用来求树上任意两点的最近相同父亲节点,有各种不同的方法,这里先介绍树上倍增求LCA(另一种我不会..) 先看一道题:(RP++) 这是翻译: 先看看朴素算法: 先依次向上查找x的祖先,存入xa数组,再依次向上查找y的祖先,与xa数组中的值比较,第一个相同的就是x,y的最近公共祖先. 每一次查找的时间复杂度是O(n) (好慢..) 而树上倍增就是将查找的次数减少,每一次都尽可能

【模板】lca的几种求法

1,倍增 vector<int>p[maxn]; int dep[maxn],f[maxn][20];//f[i][0]保存i的父亲 inline void dfs(int u,int fa,int d) { dep[u]=d;f[u][0]=fa; for(int i=0;i<p[u].size();i++) { int v=p[u][i]; if(v==fa)continue; dfs(v,u,d+1); } } inline void init(int n) { for(int j

【模板】LCA(最近公共祖先)的各种写法(施工中)

以洛谷模板题(P3379)为例. 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入样例#1:

Closest Common Ancestors【POJ1470】——LCA

Time Limit: 2000MS Memory Limit: 10000K Description Write a program that takes as input a rooted tree and a list of pairs of vertices. For each pair (u,v) the program determines the closest common ancestor of u and v in the tree. The closest common a

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

洛谷P3379 【模板】最近公共祖先(LCA)

P3379 [模板]最近公共祖先(LCA) 152通过 532提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 为什么还是超时.... 倍增怎么70!!题解好像有倍- 题面这个地方写错了 无论是用RMQ+dfs还是tarjan- 为什么我的倍增超时了 求助!为什么只有70分 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接

【BZOJ4999】This Problem Is Too Simple! 离线+树状数组+LCA

[BZOJ4999]This Problem Is Too Simple! Description 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点. Input 第一行有两个整数N,Q(1 ≤N≤ 100,000:1 ≤Q≤ 200,000),分别表示节点个数和操作个数. 下面一行N个整数,表示初始时每个节点的初