[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的最近公共祖先的深度之和)
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值