zoukankan      html  css  js  c++  java
  • 【线段树】懒标记的维护

    Comet OJ - 模拟赛 #2 Day2 圣诞节

    题目描述:

    摆在 H 君和 R 君面前的是一棵圣诞树。圣诞节就要到了,H 君和 R 君想在圣诞树上挂礼物。

    圣诞树上本来是空的,即每个节点都没有礼物。

    H 君每次会给你三个值 (u,v,w) 表示他想知道 u到 v的路径上所有值是偶数的数字之和,在这之后,他会将 u到 v的路径上所有值是奇数的数加上 w。

    R 君每次会给你三个值 (u,v,w) 表示她想知道 u 到 v 的路径上所有值是奇数的数字之和,在这之后,她会将 u 到 v 的路径上所有值是偶数的数加上 w。

    H 君和 R 君忙着挂礼物,就只能让你来回答他们的疑问了。

    输入描述:

    第一行两个整数 n和 m,表示树的节点个数和操作总数。

    第二行到第 n行,每行两个整数 u,v,表示添加一条点 u到点 v 的边。

    接下来 m 行,每行四个整数 op,u,v,w。如果 op=1表示这是一次 H 君的询问,如果 op = 2表示这是一次 R 君的询问。 

    输出描述:

    共 m行,每行一个整数表示第 i次操作的答案。

    数据:

    对所有数据保证:1<=n<=1.6*10^5, 1<=m<=10^5,op∈1,2, 1<=u,v<=n, 0<=w<=20 。

    比赛的时候能想到是树链剖分,但是懒标记的处理的不好,因为是io赛制,最后发现只拿了5分。然后当晚改来改去,隔天早上才把懒标记处理好了。

    思路:

    题目的要求就是分开处理奇偶数,但是难点在于,奇数在加上一个奇数值就会变成偶数,偶数同理。所以需要维护的是奇偶数的信息,以及在add操作的时候判断当前修改的对象 究竟是奇数还是偶数。

    我的线段树是{l,r,on,oe,sumo,sume,lao,lae}

    分别表示{左子树,右子树,区间奇数值结点个数,区间偶数值节点的个数,区间奇数值总和,区间偶数值总和,修改于奇数值节点的懒标记,修改于偶数值节点的懒标记}

    我一开始的无脑的错误是:判断当前修改对象的奇偶性时(包括add操作和down操作),分开考虑sumo和sume,然后各自操作,以及分开考虑lao和lae的传递。

    而正确做法就是综合考虑,用lazy和lazy传递对象的奇偶性,判断该对象的当前奇偶性,然后综合起来一起操作。

    哎,主要还是得想清楚。

    #include<bits/stdc++.h>
    #define debug printf("!");
    using namespace std;
    typedef long long ll;
    const int maxn=6e5+5;
    
    vector<int>p[maxn];
    
    int siz[maxn],dep[maxn],fad[maxn],son[maxn];
    int top[maxn],tid[maxn],rnk[maxn],cnt;
    inline void dfs1(int u,int fa,int d)
    {
        dep[u]=d;
        fad[u]=fa;
        siz[u]=1;
    
        for(int i=0;i<p[u].size();i++)
        {
            int v=p[u][i];
            if(v==fa)continue;
            dfs1(v,u,d+1);
            siz[u]+=siz[v];
            if(siz[v]>siz[son[u]])son[u]=v;
        }
    }
    
    inline void dfs2(int u,int t)
    {
        top[u]=t;
        tid[u]=++cnt;
        rnk[cnt]=u;
        if(!son[u])return;//当前节点不在重链上
        dfs2(son[u],t);
        for(int i=0;i<p[u].size();i++)
        {
            int v=p[u][i];
            if(v!=son[u]&&v!=fad[u])dfs2(v,v);//非重节点的top是自己
        }
    }
    
    struct kkk{
        int l,r;
        int on,en;
        ll sumo,sume;
        int lao,lae;
    }tree[maxn<<2];
    
    inline void build(int k,int l,int r)
    {
        tree[k].l=l;tree[k].r=r;
        tree[k].sumo=tree[k].sume=tree[k].lao=tree[k].lae=tree[k].en=tree[k].on=0;
        if(l==r)
        {
            tree[k].en=1;
            return;
        }
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        tree[k].en=tree[k<<1].en+tree[k<<1|1].en;
    }
    inline void down(int k)
    {
    
        tree[k<<1].sumo+=tree[k<<1].on*tree[k].lao;
        tree[k<<1].sume+=tree[k<<1].en*tree[k].lae;
        tree[k<<1|1].sumo+=tree[k<<1|1].on*tree[k].lao;
        tree[k<<1|1].sume+=tree[k<<1|1].en*+tree[k].lae;
        if(tree[k].lao%2==1&&tree[k].lae%2==1)
        {
            swap(tree[k<<1].sumo,tree[k<<1].sume);
            swap(tree[k<<1].on,tree[k<<1].en);
            swap(tree[k<<1|1].sumo,tree[k<<1|1].sume);
            swap(tree[k<<1|1].on,tree[k<<1|1].en);
        }
        else if(tree[k].lao&1)//sumo+odd=even
        {
            tree[k<<1].sume+=tree[k<<1].sumo;tree[k<<1].sumo=0;
            tree[k<<1].en+=tree[k<<1].on;tree[k<<1].on=0;
            tree[k<<1|1].sume+=tree[k<<1|1].sumo;tree[k<<1|1].sumo=0;
            tree[k<<1|1].en+=tree[k<<1|1].on;tree[k<<1|1].on=0;
        }
        else if(tree[k].lae&1)
        {
            tree[k<<1].sumo+=tree[k<<1].sume;tree[k<<1].sume=0;
            tree[k<<1].on+=tree[k<<1].en;tree[k<<1].en=0;
            tree[k<<1|1].sumo+=tree[k<<1|1].sume;tree[k<<1|1].sume=0;
            tree[k<<1|1].on+=tree[k<<1|1].en;tree[k<<1|1].en=0;
        }
    
        if(tree[k<<1].lao%2==0)tree[k<<1].lao+=tree[k].lao;
        else tree[k<<1].lao+=tree[k].lae;
        if(tree[k<<1].lae%2==1)tree[k<<1].lae+=tree[k].lao;
        else tree[k<<1].lae+=tree[k].lae;
    
        if(tree[k<<1|1].lao%2==0)tree[k<<1|1].lao+=tree[k].lao;
        else tree[k<<1|1].lao+=tree[k].lae;
        if(tree[k<<1|1].lae%2==1)tree[k<<1|1].lae+=tree[k].lao;
        else tree[k<<1|1].lae+=tree[k].lae;
        tree[k].lao=tree[k].lae=0;
    }
    inline void add(int k,int l,int r,int w,bool iso)
    {
        if(l<=tree[k].l&&tree[k].r<=r)
        {
            if(iso)//odd
            {
                if(tree[k].lao%2==0&&tree[k].lae%2==1)tree[k].lao+=w,tree[k].lae+=w;
                else if(tree[k].lao%2==0)tree[k].lao+=w;
                else if(tree[k].lae%2==1)tree[k].lae+=w;
                tree[k].sumo+=tree[k].on*w;
                if(w&1)//odd+odd=even
                {
                    tree[k].sume+=tree[k].sumo;tree[k].sumo=0;
                    tree[k].en+=tree[k].on;tree[k].on=0;
                }
            }
            else
            {
                if(tree[k].lao%2==1&&tree[k].lae%2==0)tree[k].lao+=w,tree[k].lae+=w;
                else if(tree[k].lao%2==1)tree[k].lao+=w;
                else if(tree[k].lae%2==0)tree[k].lae+=w;
                tree[k].sume+=tree[k].en*w;
                if(w&1)
                {
                    tree[k].sumo+=tree[k].sume;tree[k].sume=0;
                    tree[k].on+=tree[k].en;tree[k].en=0;
                }
            }
            return;
        }
        if(tree[k].lao||tree[k].lae)down(k);
        int mid=(tree[k].l+tree[k].r)>>1;
        if(l<=mid)add(k<<1,l,r,w,iso);
        if(r>mid)add(k<<1|1,l,r,w,iso);
        tree[k].sumo=tree[k<<1].sumo+tree[k<<1|1].sumo;
        tree[k].sume=tree[k<<1].sume+tree[k<<1|1].sume;
        tree[k].on=tree[k<<1].on+tree[k<<1|1].on;
        tree[k].en=tree[k<<1].en+tree[k<<1|1].en;
    }
    inline ll query(int k,int l,int r,int iso)
    {
        if(l<=tree[k].l&&tree[k].r<=r)
        {
            if(iso)return tree[k].sumo;
            else return tree[k].sume;
        }
        if(tree[k].lao||tree[k].lae)down(k);
        int mid=(tree[k].l+tree[k].r)>>1;
        ll ans=0;
        if(l<=mid)ans+=query(k<<1,l,r,iso);
        if(r>mid)ans+=query(k<<1|1,l,r,iso);
        return ans;
    }
    inline void addline(int x,int y,int w,bool iso)
    {
        while(top[x]!=top[y])
        {
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            add(1,tid[top[x]],tid[x],w,iso);
            x=fad[top[x]];
        }
        if(dep[x]>dep[y])swap(x,y);
        add(1,tid[x],tid[y],w,iso);
    }
    inline ll calline(int x,int y,bool iso)
    {
        ll ans=0;
        while(top[x]!=top[y])
        {
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            ans+=query(1,tid[top[x]],tid[x],iso);
            x=fad[top[x]];
        }
        if(dep[x]>dep[y])swap(x,y);
        ans+=query(1,tid[x],tid[y],iso);
        return ans;
    }
    
    
    int main()
    {
        int n,m,u,v,w,op;
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            p[u].push_back(v);
            p[v].push_back(u);
        }
        memset(son,0,sizeof(son));
        dfs1(1,0,1);
        dfs2(1,1);
        build(1,1,n);
        while(m--)
        {
            scanf("%d%d%d%d",&op,&u,&v,&w);
            if(op==1)
            {
                printf("%lld
    ",calline(u,v,0));
                addline(u,v,w,1);
            }
            else
            {
                printf("%lld
    ",calline(u,v,1));
                addline(u,v,w,0);
            }
        }
    }
  • 相关阅读:
    【题解】Luogu P4391 [BOI2009]Radio Transmission 无线传输
    kmp匹配详解
    点分治详解
    【题解】Luogu P3871 [TJOI2010]中位数
    树链剖分详解
    【题解】Luogu P3901 数列找不同
    【题解】Luogu P1503 鬼子进村
    【题解】 P2234 [HNOI2002]营业额统计
    Splay详解
    JSOI2020备考知识点复习
  • 原文地址:https://www.cnblogs.com/kkkek/p/11968774.html
Copyright © 2011-2022 走看看