您的位置:首页 > 其它

[BZOJ3626][LNOI2014][树链剖分][差分][离线处理]LCA

2016-11-13 22:10 495 查看
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。

题目名字叫LCA,其实跟LCA并没有什么关系,因为如果5e4个询问对每个点依次求LCA肯定要GG

我们首先来看LCA深度的另一种求法,假设我们要dep[LCA(u,v)],那么可以在u到根节点的所有点打上标记(权值加1),然后再统计出v到根节点的路径上的sum,这个即是要求得dep[LCA(u,v)]

不难发现,这个算法是可以叠加的,即对于询问[L,R,goal],我们可以将[L,R]的点到根的路径都打上标记,再同级goal到跟的标记总数,即为单次询问答案

那么单次询问就可以用树链剖分来处理了,时间复O(nlog*logn),再加上q次询问,显然还是无法承受

又可以想到,对于一次询问,我们是可以差分的,即ans=[1,R,goal]-[1,L-1,goal],那现在就可以进行离线处理了

从1-n不断打标记,并将询问排好序后看当前是否有询问需要处理

问题就可以解决了

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
const int maxn=5e4+5;
const int mod=201314;
int n,m,cnt,edge,cur,ans[maxn];
int to[maxn],nxt[maxn],head[maxn];
int fa[maxn],son[maxn],size[maxn],dfn[maxn],top[maxn];
struct Data
{
int pos,order,flag,goal;
bool operator<(const Data &x)const
{
return pos<x.pos;
}
}data[maxn<<1];
struct Seg
{
int L,R,sum,lazy;
}seg[maxn<<2];
#define L(x) seg[x].L
#define R(x) seg[x].R
#define sum(x) seg[x].sum
#define lazy(x) seg[x].lazy
#define Lson(x) (x<<1)
#define Rson(x) (x<<1|1)
int readint()
{
int x=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
return x;
}
void edge_add(int u,int v)
{
to[++edge]=v; nxt[edge]=head[u]; head[u]=edge;
}
void dfs1(int u)
{
size[u]=1;
for (int E=head[u];E;E=nxt[E])
{
int v=to[E];
if (v==fa[u]) continue;
dfs1(v); size[u]+=size[v];
if (size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp; dfn[u]=++cnt;
if (son[u]) dfs2(son[u],tp);
for (int E=head[u];E;E=nxt[E])
{
int v=to[E];
if (v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void build(int s,int t,int tr)
{
L(tr)=s; R(tr)=t;
if (s==t) return ;
int mid=(s+t)>>1;
build(s,mid,Lson(tr)); build(mid+1,t,Rson(tr));
}
void pushup(int tr)
{
sum(tr)=(sum(Lson(tr))+sum(Rson(tr)))%mod;
}
void pushdown(int tr)
{
lazy(Lson(tr))+=lazy(tr); lazy(Rson(tr))+=lazy(tr);
sum(Lson(tr))+=lazy(tr)*(R(Lson(tr))-L(Lson(tr))+1); sum(Lson(tr))%=mod;
sum(Rson(tr))+=lazy(tr)*(R(Rson(tr))-L(Rson(tr))+1); sum(Rson(tr))%=mod;
lazy(tr)=0;
}
void update2(int s,int t,int tr)
{
if (s==L(tr)&&t==R(tr))
{
sum(tr)+=t-s+1; sum(tr)%=mod; lazy(tr)++;
return ;
}
pushdown(tr);
int mid=(L(tr)+R(tr))>>1;
if (t<=mid) update2(s,t,Lson(tr));
else if (s>=mid+1) update2(s,t,Rson(tr));
else update2(s,mid,Lson(tr)),update2(mid+1,t,Rson(tr));
pushup(tr);
}
void update1(int u)
{
while (top[u]^1)
{
update2(dfn[top[u]],dfn[u],1);
u=fa[top[u]];
}
update2(1,dfn[u],1);
}
int query2(int s,int t,int tr)
{
if (s==L(tr)&&t==R(tr)) return sum(tr);
pushdown(tr);
int mid=(L(tr)+R(tr))>>1;
if (t<=mid) return query2(s,t,Lson(tr));
if (s>=mid+1) return query2(s,t,Rson(tr));
return query2(s,mid,Lson(tr))+query2(mid+1,t,Rson(tr));
}
int query1(int u)
{
int ret=0;
while (top[u]^1)
{
ret+=query2(dfn[top[u]],dfn[u],1); ret%=mod;
u=fa[top[u]];
}
ret+=query2(1,dfn[u],1);
return ret%mod;
}
int main()
{
n=readint(); m=readint();
for (int i=2;i<=n;i++) fa[i]=readint(),edge_add(++fa[i],i);
dfs1(1); dfs2(1,1); build(1,n,1);
for (int i=1;i<=m;i++)
{
int u=((i-1)<<1)+1,v=u+1;
data[u].pos=readint(); data[v].pos=readint()+1;
data[u].goal=data[v].goal=readint()+1;
data[u].order=data[v].order=i;
data[u].flag=-1; data[v].flag=1;
}
sort(data,data+(m<<1|1));
for (int i=1;i<=(m<<1);i++)
{
if (!data[i].pos) continue;
while (cur<data[i].pos)
{
cur++; update1(cur);
}
ans[data[i].order]+=(query1(data[i].goal)*data[i].flag);
}
for (int i=1;i<=m;i++) printf("%d",(ans[i]+mod)%mod),putchar('\n');
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: