zoukankan      html  css  js  c++  java
  • [bzoj3626][LNOI2014]LCA

    来自FallDream的博客,未经允许,请勿转载,谢谢。


    给出一个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的最近公共祖先的深度之和)

    n,q<=50000

    把每个点到根的路径上的权值都加一,询问就变成了求这个点到跟的权值和,树剖一下线段树。

    因为每次询问的点有区间,所以可以差分一下。如果要求强制在线可以用可持久化线段树

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #define MN 50000
    #define ll long long
    using namespace std;
    inline int read()
    {
        int x = 0; char ch = getchar();
        while(ch < '0' || ch > '9')  ch = getchar();
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x;
    }
    ll Ans[MN+5];
    int cnt=0,head[MN+5],n,m,fa[MN+5],mx[MN+5],dfn[MN+5],dn=0,size[MN+5],top[MN+5];
    struct Tree{int l,r,val;ll x;}T[MN*4+5];
    struct Ques{int x,id,ad;};
    struct edge{int to,next;}e[MN+5];
    vector<Ques> v[MN+5];
    inline void ins(int f,int t){e[++cnt]=(edge){t,head[f]};head[f]=cnt;}
    
    void dfs1(int x)
    {
        size[x]=1;mx[x]=0;
        for(int i=head[x];i;i=e[i].next)    
            dfs1(e[i].to),size[x]+=size[e[i].to],
            size[e[i].to]>size[mx[x]]?mx[x]=e[i].to:0; 
    }
    
    void dfs2(int x,int tp)
    {
        top[x]=tp;dfn[x]=++dn;
        if(mx[x]) dfs2(mx[x],tp);
        for(int i=head[x];i;i=e[i].next)
            if(e[i].to!=mx[x]) dfs2(e[i].to,e[i].to);    
    }
    
    void build(int x,int l,int r)
    {
        if((T[x].l=l)==(T[x].r=r)) return;
        int mid=l+r>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
    }
    
    void Mark(int x,int v)
    {
        T[x].val+=v;
        T[x].x+=1LL*(T[x].r-T[x].l+1)*v;    
    }
    
    void pushdown(int x)
    {
        Mark(x<<1,T[x].val);
        Mark(x<<1|1,T[x].val);
        T[x].val=0;    
    }
    
    void Modify(int x,int l,int r,int ad)
    {
        if(T[x].l==l&&T[x].r==r) {Mark(x,ad);return;}
        if(T[x].val) pushdown(x);    
        int mid=T[x].l+T[x].r>>1;
        if(r<=mid) Modify(x<<1,l,r,ad);
        else if(l>mid) Modify(x<<1|1,l,r,ad);
        else Modify(x<<1,l,mid,ad),Modify(x<<1|1,mid+1,r,ad);
        T[x].x=T[x<<1].x+T[x<<1|1].x; 
    }
    
    ll Query(int x,int l,int r)
    {
        if(T[x].l==l&&T[x].r==r) return T[x].x;
        if(T[x].val) pushdown(x);
        int mid=T[x].l+T[x].r>>1;    
        if(r<=mid) return Query(x<<1,l,r);
        else if(l>mid) return Query(x<<1|1,l,r);
        else return Query(x<<1,l,mid)+Query(x<<1|1,mid+1,r);
    }
    
    void Add(int x)
    {
        for(;x;x=fa[top[x]])
            Modify(1,dfn[top[x]],dfn[x],1); 
    } 
    
    ll Solve(int x)
    {
        ll sum=0;
        for(;x;x=fa[top[x]])
            sum+=Query(1,dfn[top[x]],dfn[x]);     
        return sum;
    }
    
    main()
    {
        n=read();m=read();build(1,1,n);
        for(int i=2;i<=n;++i) ins(fa[i]=read()+1,i);
        dfs1(1);dfs2(1,1);
        for(int i=1;i<=m;++i)
        {
            int l=read()+1,r=read()+1,z=read()+1;
            if(l>r) continue;
            v[r].push_back((Ques){z,i,1});
            v[l-1].push_back((Ques){z,i,-1}); 
        }
        for(int i=1;i<=n;++i)
        {
            Add(i);    
            for(int j=0;j<v[i].size();++j)
                Ans[v[i][j].id]+=v[i][j].ad*Solve(v[i][j].x);
        }
        for(int i=1;i<=m;++i) printf("%d
    ",Ans[i]%201314);
        return 0;
    }
  • 相关阅读:
    DOM-window下的常用子对象-location-刷新页面
    row_number over( partition by xx)
    linux openjdk安装
    ffmpeg直播系统
    flink 基本原理
    flink分层 api
    flink测试用例编写
    使用mybatis的动态sql解析能力生成sql
    大数据量显示问题
    vue使用日记
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj3626.html
Copyright © 2011-2022 走看看