• LNOI2014LCA(树链剖分+离线操作+前缀和)


    题意:给一棵有根树,有多组询问,询问为l r z,求下标为l到r之间的点和z的lca的深度和。

    如果我们一个一个求。emmmmm...

    考虑答案怎么产生,仔细想一想,如果我们把l到r的所有点到根都加上1,那么z到根的和就是答案。

    但这样复杂度还是爆炸,考虑如何优化?

    有一个非常有用的东西,每次操作的下标是连续的!!

    我们此时自然而然的想到前缀和,ans[i]=dis[r]-dis[l-1]。

    那我们从1-n每个点都加一次,遇到操作就加上就好了,复杂度q*log2(n)。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #define mod 201314 
    #define N 50009
    using namespace std;
    vector<int>vec[N];
    int head[N],fa[N],tot1,tot,size[N],dfn[N],son[N],top[N],top2,n,qu,ans[N];
    struct ed
    {
        int n,to;
    }an[N];
    struct scd
    {
        int a,id,tag,wo;
    }ins[N<<1];
    struct tree
    {
        int la,num;
    }tr[N<<2];
    inline void add(int u,int v)
    {
        an[++tot1].n=head[u];
        an[tot1].to=v;
        head[u]=tot1;
    }
    void dfs(int u,int f)
    {
        fa[u]=f;
        size[u]=1;
        for(int i=head[u];i;i=an[i].n)
        if(an[i].to!=f)
        {
            int v=an[i].to;
            dfs(v,u);
            size[u]+=size[v];
            if(size[son[u]]<size[v])son[u]=v;
        }
    }
    void dfs2(int u)
    {
        dfn[u]=++top2;
        if(!top[u])top[u]=u;
        if(son[u])top[son[u]]=top[u],dfs2(son[u]);
        for(int i=head[u];i;i=an[i].n)
        if(an[i].to!=fa[u]&&an[i].to!=son[u])dfs2(an[i].to);
    }
    inline void pushdown(int cnt,int l1,int l2)
    {
        (tr[cnt<<1].num+=tr[cnt].la*l1)%=mod;
        (tr[cnt<<1|1].num+=tr[cnt].la*l2)%=mod;
        tr[cnt<<1].la+=tr[cnt].la;
        tr[cnt<<1|1].la+=tr[cnt].la;
        tr[cnt].la=0; 
    }
    void dd(int cnt,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)
        {
            (tr[cnt].num+=r-l+1)%=mod;
             tr[cnt].la++;
            return;
        }
        int mid=(l+r)>>1;
        if(tr[cnt].la)pushdown(cnt,mid-l+1,r-mid);
        if(mid>=L)dd(cnt<<1,l,mid,L,R);
        if(mid<R)dd(cnt<<1|1,mid+1,r,L,R);
        (tr[cnt].num=tr[cnt<<1].num+tr[cnt<<1|1].num)%=mod;
    }
    long long qq(int cnt,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)return tr[cnt].num;
        int mid=(l+r)>>1;
        long long ans=0;
        if(tr[cnt].la)pushdown(cnt,mid-l+1,r-mid);
        if(mid>=L)ans+=qq(cnt<<1,l,mid,L,R);
        if(mid<R)ans+=qq(cnt<<1|1,mid+1,r,L,R);
        return ans%mod;
    }
    void work(int u)
    {
        while(top[1]!=top[u])
        {   
          dd(1,1,n,dfn[top[u]],dfn[u]);
          u=fa[top[u]];
        }
        dd(1,1,n,dfn[1],dfn[u]);
    }
    long long q(int u)
    {
        long long ans=0;
        while(top[1]!=top[u])
        {
            ans+=qq(1,1,n,dfn[top[u]],dfn[u]);
            u=fa[top[u]];
            ans%=mod;
        }
        ans+=qq(1,1,n,dfn[1],dfn[u]);
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&qu);
        int x,l,r,z;
        for(int i=1;i<n;++i)
         scanf("%d",&x),add(x+1,i+1);
        dfs(1,1);dfs2(1);
        for(int i=1;i<=qu;++i) 
        {
            scanf("%d%d%d",&l,&r,&z);l++;r++;z++;
            ins[++tot].a=l-1;ins[tot].id=i;ins[tot].tag=-1;ins[tot].wo=z;vec[l-1].push_back(tot);
            ins[++tot].a=r;ins[tot].id=i;ins[tot].tag=1;ins[tot].wo=z;vec[r].push_back(tot);
        }
        for(int i=1;i<=n;++i)
        {work(i);
         for(int j=0;j<vec[i].size();++j)
            {int p=vec[i][j];(ans[ins[p].id]+=ins[p].tag*q(ins[p].wo))%=mod;}
        }
        for(int i=1;i<=qu;++i)
          printf("%d
    ",(ans[i]+mod)%mod);
        return 0;
    }
  • 相关阅读:
    发一个使用 GridView 对数据小类进行分别汇总的例子
    C#根据当前时间确定日期范围(本周、本月、本季度、本年度)
    软件开发人员的作战手册
    C#服务常用继成函数说明
    ServiceController控制windows服务
    不做沙和尚
    C#多线程(二) 如何操纵一个线程转
    C#多线程(一) 多线程的相关概念
    如何为windows服务添加安装程序(转)
    用C#开发Windows服务、自动安装注册(转)
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9408044.html
Copyright © 2020-2023  润新知