zoukankan      html  css  js  c++  java
  • 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。

    由于操作可减,我们考虑离线算法,将询问Q(l,r,z)拆成Q(r,z)-Q(l-1,z)。
    我们用邻接表将相同的r串起来,那么我们考虑本题实际求什么。
    考虑节点x与节点z的LCA的深度,可以脑洞大开地想到将x到根的路径上所有点权值+1,再询问z到根的路径上的权值之和。(这个转换想想就是正确的么)
    那么sigma_{1<=i<=r}dep[LCA(i,z)]就可以将1--i到根的路径上所有点权值+1,再询问z到根的路径上的权值之和。
    离线以后用树链剖分或LCT做做即可。
    觉得用LCT写比较方便,就写了一发。
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define lc ch[x][0]
    #define rc ch[x][1]
    #define rep(s,t) for(int i=s;i<=t;i++)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    typedef long long ll;
    const int mod=201314;
    const int maxn=100010;
    struct Query {int id,z,tp;}qs[maxn];
    int ans[maxn],first[maxn],next[maxn],ToT;
    void AddQuery(int z,int r,int l,int id) {
        qs[++ToT]=(Query){id,z,1};next[ToT]=first[r];first[r]=ToT;
        qs[++ToT]=(Query){id,z,-1};next[ToT]=first[l-1];first[l-1]=ToT;
    }
    int n,q,fa[maxn],pre[maxn],ch[maxn][2],val[maxn],flip[maxn],add[maxn],s[maxn];
    ll sum[maxn];
    void maintain(int x) {
        s[x]=s[lc]+s[rc]+1;
        sum[x]=sum[lc]+sum[rc]+val[x];
        if(add[x]) sum[x]+=add[x]*(s[x]-1);
        sum[x]%=mod;
    }
    void Add(int x,int v) {
        if(!x) return;
        add[x]+=v;val[x]+=v;(sum[x]+=s[x]*v)%=mod;
    }
    void pushdown(int x) {
        if(flip[x]) {
            swap(lc,rc);flip[x]=0;
            flip[lc]^=1;flip[rc]^=1;
        }
        if(add[x]) {
            Add(lc,add[x]);Add(rc,add[x]);
            add[x]=0;
        }
    }
    void rotate(int x) {
        int y=pre[x],z=pre[y],d=ch[y][0]==x;
        ch[y][d^1]=ch[x][d];pre[ch[x][d]]=y;
        ch[z][ch[z][1]==y]=x;pre[x]=z;
        ch[x][d]=y;pre[y]=x;maintain(y);
    }
    int S[maxn],top;
    void splay(int x) {
        for(int i=x;i;i=pre[i]) S[++top]=i;
        if(top!=1) fa[x]=fa[S[top]];
        while(top) pushdown(S[top--]);
        while(pre[x]) rotate(x);
        maintain(x);
    }
    void access(int x) {
        for(int y=0;x;x=fa[x]) {
            splay(x);pre[ch[x][1]]=0;fa[ch[x][1]]=x;
            ch[x][1]=y;pre[y]=x;maintain(y=x);
        }
    }
    void makeroot(int x) {access(x);splay(x);flip[x]^=1;}
    void link(int x,int y) {makeroot(x);fa[x]=y;}
    int query(int x,int y) {
        makeroot(x);access(y);splay(y);
        return sum[y];
    }
    void update(int x,int y) {
        makeroot(x);access(y);splay(y);
        Add(y,1);
    }
    int main() {
        n=read();q=read();val[1]=1;
        rep(2,n) link(read()+1,i);
        rep(1,q) AddQuery(read()+1,read()+1,read()+1,i);
        rep(1,n) {
            if(i!=1) update(1,i);
            for(int j=first[i];j;j=next[j]) ans[qs[j].id]+=query(qs[j].z,1)*qs[j].tp;
        }
        rep(1,q) printf("%d
    ",(ans[i]+mod)%mod);
        return 0;
    }
    View Code
  • 相关阅读:
    iis 7.5应用程序池自动停止
    百度云推送
    两点经纬度之间距离计算
    集合已修改;可能无法执行枚举操作。
    【百度地图】- 学习.1
    阿里云服务器下安装LAMP环境(CentOS Linux 6.3)
    微信相关内容
    阿里云服务器下安装LAMP环境(CentOS Linux 6.3)
    php面试相关
    MySQL权限管理
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/4622057.html
Copyright © 2011-2022 走看看