zoukankan      html  css  js  c++  java
  • COJ990 WZJ的数据结构(负十)

    DFS序(带入栈出栈标记):

    对于一个节点,我们用L[i]和R[i]表示它入栈和出栈的时间。这样[L[i],R[i]]就表示了以i为根的区间。

    我们还要将入栈的符号为+,出栈的符号为-,那么令V[i]=sig[i]*val[i]。

    这样有什么好处呢?

    1.对于一个节点x到根的节点val权值和,等于Sum{V[1,R[x]]}。

    2.对于将一个子树所有值+v,等于将[L[i],R[i]]的所有V值+v

    这便是一个经典的线段树

    但是这样有什么局限性呢?答案必须满足区间加减法(例如这道题DFS序无法求最大值)。

    否则怎么做呢?树链剖分!!!!!!!!!!!!!

    DFS序好像是Fenwich,树链剖分好像是Splay

    -------------------------------------------------------------------------------------------------------

    暑假出的题,其实是从一道BZOJ的题摘下来的,原题还有换根操作,只能用splay动态维护DFS序列。

    恩先放一个之前写的DFS序列(带入栈出栈标记)+线段树版本的:(写得丑请不要介意)

    询问O(logn)修改O(logn)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=200010;
    int n,m,first[maxn],next[maxn],v[maxn],e;
    void AddEdge(int a,int b)
    {
         v[++e]=b;
         next[e]=first[a];
         first[a]=e;
    }
    int F[maxn],L[maxn],s[maxn],val[maxn],tot;
    void dfs(int x)
    {
         F[x]=++tot; s[tot]=1;
         for(int i=first[x];i;i=next[i]) dfs(v[i]);
         L[x]=++tot; s[tot]=-1;
    }
    typedef long long LL;
    LL sumv[maxn*3],addv[maxn*3],sz[maxn*3];
    void maintain(int o,int L,int R)
    {
         sumv[o]=0;
         if(L<R) sumv[o]=sumv[o<<1]+sumv[(o<<1)|1];
         sumv[o]+=addv[o]*sz[o];
    }
    void build(int o,int L,int R)
    {
         if(L==R) addv[o]=val[L],sz[o]=s[L];
         else
         {
             int M=L+R>>1,lc=o<<1,rc=lc|1;
             build(lc,L,M); build(rc,M+1,R);
             sz[o]=sz[lc]+sz[rc];
         }
         maintain(o,L,R);
    }
    int ql,qr,va;
    LL query(int o,int L,int R,int add)
    {
        if(ql<=L&&R<=qr) return sumv[o]+add*sz[o];
        else
        {
            int M=L+R>>1,lc=o<<1,rc=lc|1;
            LL ans=0;
            if(ql<=M) ans+=query(lc,L,M,add+addv[o]);
            if(qr>M) ans+=query(rc,M+1,R,add+addv[o]);
            return ans;
        }
    }
    void update(int o,int L,int R)
    {
         if(ql<=L&&R<=qr) addv[o]+=va;
         else
         {
            int M=L+R>>1,lc=o<<1,rc=lc|1;
            if(ql<=M) update(lc,L,M);
            if(qr>M) update(rc,M+1,R);
        }
        maintain(o,L,R);
    }
    int main()
    {
        int p;
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
        {
            scanf("%d",&p);
            AddEdge(p,i);
        }
        dfs(1);
        for(int i=1;i<=n;i++) scanf("%d",&p),val[L[i]]=val[F[i]]=p;
        build(1,1,n*2);
        scanf("%d",&m);
        char cmd[4];
        while(m--)
        {
           scanf("%s",cmd);
           if(cmd[0]=='Q')
           {
               scanf("%d",&qr);ql=1;qr=F[qr];
               printf("%lld
    ",query(1,1,n*2,0));
           }
           else
           {
               scanf("%d%d",&ql,&va);
               qr=L[ql]; ql=F[ql];
               update(1,1,n*2);
           }
        }
        return 0;
    }
    View Code

    发现可以用树链剖分做,昨天写了一下,WA掉N发,还是线段树写错了。

    询问O(log^2n)修改O(logn)

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    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 maxn=100010;
    int n,first[maxn],to[maxn],next[maxn],e;
    void AddEdge(int u,int v){to[++e]=v;next[e]=first[u];first[u]=e;}//单向边就可以了 
    int fa[maxn],siz[maxn],son[maxn];
    void dfs(int x)//……………… 
    {
        siz[x]=1;
        for(int i=first[x];i;i=next[i])//因为是单向边,下同 
        {
            fa[to[i]]=x;dfs(to[i]);
            siz[x]+=siz[to[i]];
            if(siz[son[x]]<siz[to[i]]) son[x]=to[i];
        }
    }
    int top[maxn],pos[maxn],mx[maxn],sz;
    void build(int x,int tp)//建树时记录DFS序的区间 
    {
        top[x]=tp;pos[x]=++sz;
        if(son[x]) build(son[x],tp);
        for(int i=first[x];i;i=next[i]) if(to[i]!=son[x]) build(to[i],to[i]);
        mx[x]=sz;//右端点 
    }
    int ql,qr;
    ll A[maxn],sumv[maxn*3],addv[maxn*3],val;
    ll query(int o,int l,int r,ll add)
    {
        if(ql<=l&&r<=qr) return sumv[o]+add*(r-l+1);
        int mid=l+r>>1,lc=o<<1,rc=lc|1;ll ans=0;
        if(ql<=mid) ans+=query(lc,l,mid,add+addv[o]);
        if(qr>mid) ans+=query(rc,mid+1,r,add+addv[o]);
        return ans;
    }
    void build(int o,int l,int r)
    {
        int mid=l+r>>1,lc=o<<1,rc=lc|1;
        if(l==r) sumv[o]=addv[o]=A[l];//注意! 
        else
        {
            build(lc,l,mid);build(rc,mid+1,r);
            sumv[o]=sumv[lc]+sumv[rc];
        }
    }
    void update(int o,int l,int r)
    {
        int mid=l+r>>1,lc=o<<1,rc=lc|1;
        if(ql<=l&&r<=qr) addv[o]+=val;
        else
        {
            if(ql<=mid) update(lc,l,mid);
            if(qr>mid) update(rc,mid+1,r);
        }
        sumv[o]=0;
        if(l<r) sumv[o]=sumv[lc]+sumv[rc];////注意叶节点信息,本蒟蒻就在这里WA了N发 TAT 
        sumv[o]+=(r-l+1)*addv[o];
    }
    void query(int x)
    {
        ll ans=0;
        while(top[x]!=1)//不停向根节点走 
        {
            ql=pos[top[x]];qr=pos[x];ans+=query(1,1,n,0);
            x=fa[top[x]];
        }
        ql=1;qr=pos[x];ans+=query(1,1,n,0);
        printf("%lld
    ",ans);
    }
    int main()
    {
        n=read();
        for(int i=2;i<=n;i++) AddEdge(read(),i);
        dfs(1);build(1,1);
        for(int i=1;i<=n;i++) A[pos[i]]=read();
        build(1,1,n);
        int q=read();
        while(q--)
        {
            char tp=getchar();
            while(!isalpha(tp)) tp=getchar();
            int x=read();
            if(tp=='Q') query(x);
            else
            {
                val=read();ql=pos[x];qr=mx[x];
                update(1,1,n);
            }
        }
        return 0;
    }
    View Code

     其实用DFS序就可以解决了。对于询问时,我们考虑x到根路径上一次对y子树的修改,贡献是(depy-depx+1)*vx,变形后可得depy*sigma(vx)-sigma((depx-1)*vx),用树状数组分别维护两个值就可以了。初始同理,请读者自行分析。O(logn)

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #define rep(i,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;
    }
    const int maxn=100010;
    int n,q,root,first[maxn],next[maxn],to[maxn],e;
    void AddEdge(int u,int v) { 
        to[++e]=v;next[e]=first[u];first[u]=e;
    }
    int L[maxn],R[maxn],cnt,dep[maxn];
    void dfs(int x) {
         L[x]=++cnt;
         ren dep[to[i]]=dep[x]+1,dfs(to[i]);
         R[x]=cnt;
    }
    typedef long long LL;
    struct Fenwich {
        LL sumv[maxn];
        void add(int x,LL v) {for(;x<=n;x+=x&-x) sumv[x]+=v;}
        LL sum(int x) {LL ret=0;for(;x;x-=x&-x) ret+=sumv[x];return ret;}
    }tree1,tree2;
    int main() {
        n=read();
        rep(i,2,n) AddEdge(read(),i);
        dfs(1);
        rep(x,1,n) {
            int v=read();
            tree2.add(L[x],-v);tree2.add(R[x]+1,v);  
        }
        q=read();
        while(q--) {
            char cmd=getchar();
            while(!isalpha(cmd)) cmd=getchar();
            if(cmd=='Q') {
                int x=read();
                printf("%lld
    ",tree1.sum(L[x])*dep[x]-tree2.sum(L[x]));             
            }           
            else {
                int x=read(),v=read();
                tree1.add(L[x],v);tree1.add(R[x]+1,-v);
                tree2.add(L[x],(LL)(dep[x]-1)*v);tree2.add(R[x]+1,(LL)(1-dep[x])*v);    
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    jperf windows
    Eclipse+Maven命令创建webapp项目<三>
    Eclipse+Maven创建webapp项目<二>
    Eclipse+Maven创建webapp项目<一>
    在java中定义有几种类加载器
    JAVA创建对象有哪几种方式 ?
    js创建对象的几种常用方式小结(推荐)
    maven安装以及eclipse配置maven
    MyEclipse 10.0安装及激活步骤
    jdk下载网址
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/4507543.html
Copyright © 2011-2022 走看看