zoukankan      html  css  js  c++  java
  • BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 2050  Solved: 817
    [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


    orz gconeice

    显然,暴力求解的复杂度是无法承受的。
    考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。

    z的LCA只能在z到根的路径上,统计到根的路径上每个点被作为LCA几次

    很巧妙的转换,[l,r]所有点到根路径+1,然后询问z到根路径权值

    显然树链剖分

    多组询问,考虑将询问差分,[1, r] − [1, l − 1],然后离线询问的两个端点,每加到一个now将l-1或者r是now到询问都问一下

    //
    //  main.cpp
    //  bzoj4196
    //
    //  Created by Candy on 2017/1/2.
    //  Copyright © 2017年 Candy. All rights reserved.
    //
    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define lc o<<1
    #define rc o<<1|1
    #define m ((l+r)>>1)
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    const int N=1e5+5,MOD=201314;
    typedef long long ll;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    int n,Q,u,l,r;
    char s[20];
    struct ques{
        int z,al,ar;
    }q[N];
    struct que{
        int x,id,isl;
        bool operator <(const que &r)const{return x<r.x;}
    }a[N];
    int p;
    struct edge{
        int v,ne,c,f;
    }e[N<<1];
    int cnt,h[N];
    inline void ins(int u,int v){
        cnt++;
        e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
    }
    
    int deep[N],fa[N],tid[N],tot,top[N],mx[N],size[N];
    void dfs(int u){
        size[u]=1;
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v;
            if(v==fa[u]) continue;
            fa[v]=u;deep[v]=deep[u]+1;
            dfs(v);
            size[u]+=size[v];
            if(size[v]>size[mx[u]]) mx[u]=v;
        }
    }
    void dfs(int u,int anc){
        if(!u) return;
        tid[u]=++tot;
        top[u]=anc;
        dfs(mx[u],anc);
        for(int i=h[u];i;i=e[i].ne)
            if(e[i].v!=mx[u]&&e[i].v!=fa[u]) dfs(e[i].v,e[i].v);
    }
    
    struct node{
        int sum,add;
    }t[N<<2];
    inline void merge(int o){
        t[o].sum=t[lc].sum+t[rc].sum;
    }
    inline void paint(int o,int l,int r,int d){
        t[o].sum+=d*(r-l+1);
        t[o].add+=d;
    }
    inline void pushDown(int o,int l,int r){
        if(t[o].add){
            paint(lson,t[o].add);
            paint(rson,t[o].add);
            t[o].add=0;
        }
    }
    inline void segAdd(int o,int l,int r,int ql,int qr,int d){//printf("add %d %d %d
    ",o,l,r);
        if(ql<=l&&r<=qr) paint(o,l,r,d);
        else{
            pushDown(o,l,r);
            if(ql<=m) segAdd(lson,ql,qr,d);
            if(m<qr) segAdd(rson,ql,qr,d);
            merge(o);
        }
    }
    inline int segQue(int o,int l,int r,int ql,int qr){//printf("que %d %d %d %d %d
    ",o,l,r,ql,qr);
        if(ql<=l&&r<=qr) return t[o].sum;
        else{
            pushDown(o,l,r);
            int ans=0;
            if(ql<=m) ans+=segQue(lson,ql,qr);
            if(m<qr) ans+=segQue(rson,ql,qr);
            return ans;
        }
    }
    void build(int o,int l,int r){
        if(l==r) paint(o,l,r,0);
        else{
            build(lson);
            build(rson);
        }
    }
    
    void add(int x,int y,int d){
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]]) swap(x,y);
            segAdd(1,1,n,tid[top[x]],tid[x],d);
            x=fa[top[x]];
        }
        if(tid[x]>tid[y]) swap(x,y);
        segAdd(1,1,n,tid[x],tid[y],d);
    }
    int query(int x,int y){
        int ans=0;
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]]) swap(x,y);
            ans+=segQue(1,1,n,tid[top[x]],tid[x]);ans%=MOD;
            x=fa[top[x]];
        }
        if(tid[x]>tid[y]) swap(x,y);
        ans+=segQue(1,1,n,tid[x],tid[y]);ans%=MOD;
        return ans;
    }
    int main(){
        n=read();Q=read();
        for(int i=2;i<=n;i++) u=read()+1,ins(u,i);
        dfs(1);dfs(1,1);
        build(1,1,n);
        for(int i=1;i<=Q;i++){
            l=read();r=read()+1;q[i].z=read()+1;
            a[++p].x=l;a[p].id=i;a[p].isl=1;
            a[++p].x=r;a[p].id=i;a[p].isl=0;
        }
        sort(a+1,a+1+p);
        int now=1;
        for(int i=1;i<=p;i++){//printf("a %d %d %d
    ",a[i].x,a[i].id,a[i].isl);
            while(now<=a[i].x) add(1,now,1),now++;
            int _=a[i].id;
            if(a[i].isl) q[_].al=query(1,q[_].z);
            else q[_].ar=query(1,q[_].z);
        }
        for(int i=1;i<=Q;i++) printf("%d
    ",(q[i].ar-q[i].al+MOD)%MOD);
    }

    在线的话可以用主席树,在dfs序上建主席树

    这样每个线段树与历史版本的差距是一个节点,也就是一个点到根的路径,有logn段,每段有logn个新开节点,复杂度log^2n

    好麻烦还要带标记啊

     [2017-01-04 00:01:51]

     我太弱了,不是M就是R,不玩了

  • 相关阅读:
    Java学习
    Java学习
    Java学习
    Java学习
    Java学习
    Java学习
    Java学习
    springboot之RabbitMQ
    IIS自动发布脚本
    存储器
  • 原文地址:https://www.cnblogs.com/candy99/p/6246803.html
Copyright © 2011-2022 走看看