zoukankan      html  css  js  c++  java
  • bzoj3626 [LNOI2014]LCA——树链剖分

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3626

    思路很巧妙,把区间换成前缀和相减;

    把 l ~ r 到根路径上的点的点权都+1,然后 z 到根求和,就是 z 与 l  ~ r 每个点 lca 深度的和;

    这里若要用前缀和,则需要把询问离线排序;

    然后上树链剖分,修改和求和线段树解决即可。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const maxn=50005,mod=201314;
    int n,m,fa[maxn],head[maxn],ct,dfn[maxn],tim;
    int top[maxn],to[maxn],siz[maxn],tl[maxn],tr[maxn];
    struct N{
        int to,next;
        N(int t=0,int n=0):to(t),next(n) {}
    }edge[maxn<<1];
    struct T{ll s,lzy,size;}t[maxn<<2];
    struct Q{int l,r,z,bh;ll ansl,ansr;}q[maxn];
    void add(int x,int y){edge[++ct]=N(y,head[x]);head[x]=ct;}
    bool cmp(int x,int y){return q[x].l<q[y].l;}
    bool cmp2(int x,int y){return q[x].r<q[y].r;}
    int rd()
    {
        int ret=0;char ch=getchar();
        while(ch<'0'||ch>'9')ch=getchar();
        while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
        return ret;
    }
    void dfs(int x,int f)
    {
        siz[x]=1;
        for(int i=head[x];i;i=edge[i].next)
        {
            int u=edge[i].to;
            if(u==f)continue;
            dfs(u,x); siz[x]+=siz[u];
            if(siz[u]>siz[to[x]])to[x]=u;
        }
    }
    void dfs2(int x)
    {
        dfn[x]=++tim;
        if(to[x])top[to[x]]=top[x],dfs2(to[x]);
        for(int i=head[x];i;i=edge[i].next)
        {
            int u=edge[i].to;
            if(u==fa[x]||u==to[x])continue;
            top[u]=u; //top[u]!=x !!!
            dfs2(u);
        }
    }
    void pushup(int x)
    {
        t[x].size=t[x<<1].size+t[x<<1|1].size;
        t[x].s=t[x<<1].s+t[x<<1|1].s;
    }
    void pushdown(int x)
    {
        if(!t[x].lzy)return;
        int ls=(x<<1),rs=(x<<1|1);
        ll l=t[x].lzy;t[x].lzy=0;
        t[ls].s+=l*t[ls].size; t[ls].lzy+=l;
        t[rs].s+=l*t[rs].size; t[rs].lzy+=l;
    }
    void build(int x,int l,int r)
    {
        if(l==r){t[x].size=1; return;}
        int mid=((l+r)>>1);
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
        pushup(x);
    }
    void update(int x,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)
        {
            t[x].s+=t[x].size; t[x].lzy++;
            return;
        }
        pushdown(x);
        int mid=((l+r)>>1);
        if(mid>=L)update(x<<1,l,mid,L,R);
        if(mid<R)update(x<<1|1,mid+1,r,L,R);
        pushup(x);
    }
    void work(int x)//x 到 rt 路径上点权+1 
    {
        while(x)
        {
            update(1,1,n,dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        }
    //    update(1,1,n,dfn[rt],dfn[rt]);
    }
    ll query(int x,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)return t[x].s;
        int mid=((l+r)>>1); ll ret=0;
        pushdown(x);
        if(mid>=L)ret+=query(x<<1,l,mid,L,R);
        if(mid<R)ret+=query(x<<1|1,mid+1,r,L,R);
        return ret;
    }
    ll qry(int x)
    {
        ll ret=0;
        while(x)
        {
            ret+=query(1,1,n,dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        }
        return ret;
    }
    int main()
    {
        n=rd();m=rd();
        for(int i=2;i<=n;i++){fa[i]=rd()+1; add(fa[i],i); add(i,fa[i]);}
        for(int i=1;i<=m;i++)
        {
            q[i].l=rd()+1; q[i].r=rd()+1; q[i].z=rd()+1;
            tl[i]=i; tr[i]=i;
        }
        sort(tl+1,tl+m+1,cmp);
        sort(tr+1,tr+m+1,cmp2);
        dfs(1,0); top[1]=1; dfs2(1); build(1,1,n);
        int dl=1,dr=1;
        while(q[tl[dl]].l==1)dl++;
        for(int i=1;i<=n;i++)
        {
            work(i);
            while(q[tl[dl]].l-1==i&&dl<=m)
            {
                q[tl[dl]].ansl=qry(q[tl[dl]].z);//不套 dfn !!! 
                dl++;
            }
            while(q[tr[dr]].r==i&&dr<=m)
            {
                q[tr[dr]].ansr=qry(q[tr[dr]].z);
                dr++;
            }
        }
        for(int i=1;i<=m;i++)
            printf("%lld
    ",(q[i].ansr-q[i].ansl)%mod);
        return 0;
    }
  • 相关阅读:
    在Repeater的FooterTemplate显示某列总计
    对数据库数据操作,工厂方法设计模式(Factory Method)
    网页(aspx)与用户控件(ascx)交互与逻辑处理
    使用反射把用户控件(ASCX)传至网页(ASPX)
    软件研发公司,外观设计模式(Facade)
    看菜谱点餐,迭代设计模式(Iterator)
    There is not enough space on the disk.
    站点某些网页想显示母版页内的用户控件,某些网页不想显示,怎样实现
    C#反射(Reflection)对类的属性get或set值
    MS access 数据定时导入MS SQL Server
  • 原文地址:https://www.cnblogs.com/Zinn/p/9185157.html
Copyright © 2011-2022 走看看