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

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3626

    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,然而好像并没有什么关系。题目查询的是在l到r区间中,与z的最近公共祖先的深度之和。然后很明显可以知道两点到根节点经过的共同节点个数为其公共祖先的深度,比如样例中4和5共同经过1和2,而他们的最近公共祖先为2,其深度为2。然后求l到r之间的,可以转换为[1,l]-[1,r-1]的差。那么就可以对于查询离线,把询问分成两部分,按值来排序,比如[1,4,3],就分成[0,3]和[4,3]。查询时把需要加的点一个个加进去,当到达一个点恰好为某个询问的值,那么就可以把这个询问的答案来更新一下了。值的定义为分成两部分后区间的右端点,左端点恒为1。

    代码

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<ctime>
    #include<queue>
    #include<vector>
    #include<algorithm>
    #define mo 201314
    #define N 300010
    using namespace std;
    long long sum,ans[N];
    int tot,p,q,n,u,v;
    long long lazy[N],b[N];
    int siz[N],fa[N],son[N],dep[N],head[N],top[N],w[N];
    struct h1{int next,to;}tu[N];
    struct data{int l,f,w,wei;}a[2*N];
    int getint()
    {
        int res=0,w=1;
        char ch=getchar();
        while ((ch>'9' || ch<'0')&&ch!='-') ch=getchar();
        if (ch=='-') w=-1,ch=getchar();
        while (ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
        return res*w;
    }
    void link(int x,int y)
    {
        tu[++sum].next=head[x]; head[x]=sum; tu[sum].to=y;
    }
    void dfs1(int p)
    {
        siz[p]=1;
        for (int i=head[p];i;i=tu[i].next)
            {
                int v=tu[i].to;
                if (v==fa[p]) continue;
                dep[v]=dep[p]+1;
                dfs1(v); siz[p]+=siz[v];                                    
                if (siz[v]>siz[son[p]]) son[p]=v;
            }
    }
    void dfs2(int p,int t)
    {
        top[p]=t; w[p]=++tot;
        if (son[p]) dfs2(son[p],t);
        for (int i=head[p];i;i=tu[i].next)
            if (tu[i].to!=son[p]&&tu[i].to!=fa[p]) dfs2(tu[i].to,tu[i].to);
    }
    bool cmp(data a,data b) {return a.l<b.l;}
    void tree_add(int x,int l,int r,int L,int R)
    {
        if (l==L&&r==R) lazy[x]=(lazy[x]+1)%mo;
        else
            {
                b[x]=(b[x]+R-L+1)%mo;
                int mid=(l+r)>>1;
                if (L>mid) tree_add((x<<1)+1,mid+1,r,L,R);
                else if (R<=mid) tree_add(x<<1,l,mid,L,R);
                else tree_add(x<<1,l,mid,L,mid),tree_add((x<<1)+1,mid+1,r,mid+1,R);
            }
    }
    void tree_find(int x,int l,int r,int L,int R)
    {
        if (l==L&&r==R) sum=(sum+b[x]+1LL*lazy[x]*(r-l+1))%mo;
        else
            {
                int mid=(l+r)>>1;
                lazy[x<<1]=(lazy[x<<1]+lazy[x])%mo;
                lazy[(x<<1)+1]=(lazy[(x<<1)+1]+lazy[x])%mo;
                b[x]=(b[x]+1LL*lazy[x]*(r-l+1))%mo;
                lazy[x]=0;
                if (R<=mid) tree_find(x<<1,l,mid,L,R);
                else if (L>mid) tree_find((x<<1)+1,mid+1,r,L,R);
                else tree_find(x<<1,l,mid,L,mid),tree_find((x<<1)+1,mid+1,r,mid+1,R);
            }
    }
    void add(int p)
    {
        while (p!=0)
            {
                tree_add(1,1,n,w[top[p]],w[p]);
                p=fa[top[p]];
            }
    }
    void find(int p)
    {
        while (p!=0)
            {
                tree_find(1,1,n,w[top[p]],w[p]);
                p=fa[top[p]];
            }
    }
    int main()
    {
        n=getint(); q=getint();
        for (int i=2;i<=n;i++)
            {
                p=getint()+1;
                fa[i]=p; link(p,i);
            }
        dep[1]=1; dfs1(1);
        dfs2(1,1); tot=0;
        for (int i=1;i<=q;i++)
            {
                u=getint()+1; v=getint()+1; p=getint()+1;
                a[++tot].l=u-1; a[tot].f=-1; a[tot].wei=i; a[tot].w=p;
                a[++tot].l=v; a[tot].f=1; a[tot].wei=i; a[tot].w=p;
            }
        sort(a+1,a+1+tot,cmp);
        u=0; 
        for (int i=1;i<=tot;i++)
            {
                while (u<a[i].l) u++,add(u);
                sum=0; find(a[i].w);
                ans[a[i].wei]=(ans[a[i].wei]+1LL*sum*a[i].f+mo)%mo;
            }
        for (int i=1;i<=q;i++)
            printf("%lld
    ",ans[i]);
        return 0;
    }
    转载请联系博主!!!
  • 相关阅读:
    ESlint 格式化代码 备忘
    css 点击样式,水波纹(记录备用)
    RabbitMq 报错记录
    sql For xml path('') 备忘
    .net core Area独立成单独的dll文件
    刷shipid 简便方法
    实际操作--create DB link
    POS VB
    设置PL/SQL 快捷键
    要开始学习C#
  • 原文地址:https://www.cnblogs.com/xiaoqiang200015/p/6016006.html
Copyright © 2011-2022 走看看