zoukankan      html  css  js  c++  java
  • 洛谷 P4211 [LNOI2014]LCA

    洛谷 P4211 [LNOI2014]LCA

    洛谷传送门

    题目描述

    给出一个 nn 个节点的有根树(编号为 00 到 n-1n−1,根节点为 00)。

    一个点的深度定义为这个节点到根的距离 +1+1。

    设 dep[i]dep[i] 表示点i的深度,LCA(i,j)LCA(i,j) 表示 ii 与 jj 的最近公共祖先。

    有 qq 次询问,每次询问给出 l r zl r z,求 sum_{i=l}^r dep[LCA(i,z)]∑i=lrdep[LCA(i,z)] 。

    输入格式

    第一行 22 个整数,n, qn,q

    接下来 n-1n−1 行,分别表示点 11 到点 n-1n−1 的父节点编号。

    接下来 qq 行,每行 33 个整数,l, r, zl,r,z

    输出格式

    输出 qq 行,每行表示一个询问的答案。每个答案对 201314201314 取模输出。


    题解:

    题目很简洁。一看就是好题。

    首先,我们可以得到,任意节点和已知节点的LCA一定在已知节点到根节点的这条路径上。

    我们可以深入思考:

    深度是什么?深度就是当前点上面(包括自己)还有几个节点。那么我们完全可以把深度统计变成路径统计。既然deep[LCA]是LCA到根节点的距离,那么,求和的时候,我们完全可以对于([l,r])区间,都把根节点到当前节点的路径的节点们权值全部加一。那么处理完毕之后,只需要跑目标节点(z)到树根的路径和就行。

    芜湖,链上修改+查询。树链剖分架线段树。

    分析复杂度:这样的复杂度,一次是(O(nlog^2))的,(n)次就是(O(n^2log^2))

    这个数据范围是(O(nlog^2))的数据范围,那么我们想着如何优化掉一个n。

    我们发现,(n^2log^2)算法多出来的n消耗在重构线段树上,那么,如果我们连续处理每个询问呢?想到离线区间排序之后连续处理。但是这样的处理无法同时维护两个端点的变化。于是我们尝试着把一个区间端点固定为1.因为以上的性质满足区间减法,也就是([1,r]-[1,l)),所以可以通过这样的优化,只跑一遍修改即可。优化了一个n。

    具体实现详见代码:

    #include<cstdio>
    #include<algorithm>
    #define int long long
    #define lson pos<<1
    #define rson pos<<1|1
    using namespace std;
    const int maxn=5*1e5+10;
    const int mod=201314;
    char *p1,*p2,buf[100000];
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
    int read()
    {
        int x=0,f=1;
        char ch=nc();
        while(ch<48||ch>57)
            f=-1,ch=nc();
        while(ch>=48&&ch<=57)
            x=x*10+ch-48,ch=nc();
        return x*f;
    }
    int n,m;
    int fa[maxn],ans[maxn];
    int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
    int cnt;
    int deep[maxn],size[maxn],son[maxn],top[maxn],id[maxn],num;
    int tree[maxn<<2],lazy[maxn<<2];
    struct node
    {
        bool flag;
        int id,r,z;
    }q[maxn<<1];
    bool cmp(node a,node b)
    {
        return a.r<b.r;
    }
    void add(int x,int y)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    void dfs1(int x,int f)
    {
        deep[x]=deep[f]+1;
        size[x]=1;
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(y==f)
                continue;
            dfs1(y,x);
            size[x]+=size[y];
            if(!son[x]||size[y]>size[son[x]])
                son[x]=y;
        }
    }
    void dfs2(int x,int t)
    {
        top[x]=t;
        id[x]=++num;
        if(!son[x])
            return;
        dfs2(son[x],t);
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(y==fa[x]||y==son[x])
                continue;
            dfs2(y,y);
        }
    }
    void mark(int pos,int l,int r,int k)
    {
        tree[pos]=(tree[pos]+(r-l+1)*k)%mod;
        lazy[pos]+=k;
    }
    void pushdown(int pos,int l,int r)
    {
        int mid=(l+r)>>1;
        mark(lson,l,mid,lazy[pos]);
        mark(rson,mid+1,r,lazy[pos]);
        lazy[pos]=0;
    }
    void update(int pos,int l,int r,int x,int y)
    {
        int mid=(l+r)>>1;
        if(x<=l && r<=y)
        {
            mark(pos,l,r,1);
            return;
        }
        if(lazy[pos])
            pushdown(pos,l,r);
        if(x<=mid)
            update(lson,l,mid,x,y);
        if(y>mid)
            update(rson,mid+1,r,x,y);
        tree[pos]=(tree[lson]+tree[rson])%mod;
    }
    void upd_chain(int x,int y)
    {
        while(top[x]!=top[y])
        {
            if(deep[top[x]]<deep[top[y]])
                swap(x,y);
            update(1,1,n,id[top[x]],id[x]);
            x=fa[top[x]];
        }
        if(deep[x]<deep[y])
            swap(x,y);
        update(1,1,n,id[y],id[x]);
    }
    int query(int pos,int l,int r,int x,int y)
    {
        int ret=0;
        int mid=(l+r)>>1;
        if(x<=l && r<=y)
            return tree[pos]%mod;
        if(lazy[pos])
            pushdown(pos,l,r);
        if(x<=mid)
            ret=(ret+query(lson,l,mid,x,y))%mod;
        if(y>mid)
            ret=(ret+query(rson,mid+1,r,x,y))%mod;
        return ret%mod;
    }
    int q_chain(int x,int y)
    {
        int ret=0;
        while(top[x]!=top[y])
        {
            if(deep[top[x]]<deep[top[y]])
                swap(x,y);
            ret=(ret+query(1,1,n,id[top[x]],id[x]))%mod;
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])
            swap(x,y);
        ret=(ret+query(1,1,n,id[x],id[y]))%mod;
        return ret%mod;
    }
    signed main()
    {
        scanf("%lld%lld",&n,&m);
        for(int i=2;i<=n;i++)
        {
            scanf("%lld",&fa[i]);
            fa[i]++;
            add(fa[i],i);
            add(i,fa[i]);
        }
        dfs1(1,0);
        dfs2(1,1);
        for(int i=1;i<=m;i++)
        {
            int l,r,z;
            scanf("%lld%lld%lld",&l,&r,&z);
            r++,z++;
            q[++cnt].flag=0,q[cnt].id=i,q[cnt].r=l,q[cnt].z=z;
            q[++cnt].flag=1,q[cnt].id=i,q[cnt].r=r,q[cnt].z=z;
        }
        sort(q+1,q+cnt+1,cmp);
        q[0].r=0;
        for(int i=1;i<=cnt;i++)
        {
            for(int j=q[i-1].r+1;j<=q[i].r;j++)
                upd_chain(1,j);
            if(!q[i].flag)
                ans[q[i].id]=(ans[q[i].id]-q_chain(1,q[i].z)+mod)%mod;
            else
                ans[q[i].id]=(ans[q[i].id]+q_chain(1,q[i].z))%mod;
        }
        for(int i=1;i<=m;i++)
            printf("%lld
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    myDOM
    13模块
    12异常
    11面向对象
    10文件操作
    蓝桥杯心得
    生物芯片-2014国赛Java语言C组四题
    奇妙的数字-2015省赛C语言A组第三题
    方程整数解-2015省赛C语言A组第一题
    机器人数目-2015决赛Java语言C组第一题
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13791431.html
Copyright © 2011-2022 走看看