zoukankan      html  css  js  c++  java
  • [ZJOI2019]线段树

    首先要注意到复制出的两颗棵段树一个被修改一个不被修改

    所以实际上就是枚举了是否进行每个操作的(2^m)种情况

    肯定不可能枚举出来

    我们考虑对每个节点分别统计答案

    (f_x)表示当前节点(x)的所有情况下的(Tag)

    考虑每次加入一个操作对其(Tag)的影响

    设当前为第(t)个操作

    即修改的(2^{t-1})棵线段树中

    有多少棵的(x)节点的(Tag)(1)

    一共只有(4)种情况

    1. (Tag)直接被赋值为(1)(在这个点打标记)
    2. (Tag)直接被赋值为(0)(对该点进行了pushdown操作)
    3. (Tag)没有被修改(根本没有访问这个节点)
    4. (Tag)继承自父亲节点(对该点的父节点进行了pushdown操作但没有修改这个节点)

    考虑(f_x)的改变量

    对于前(3)种情况

    (f_x)的改变是明显的

    第一种(f_x+=2^{t-1})

    第二种(f_x+=0)

    第三种(f_x*=2)

    第四种比较复杂

    (f_x+=使这个点到根有至少一个节点的Tag为1的方案数)

    注意自己有标记也是可以的

    因为只有(1)的标记会下传

    设这个量为(g_x)

    考虑(g_x)的维护

    如果在线段树上对该点进行了pushdown操作

    那么新增的(2^{t-1})棵树的这个节点根的路径上都不会有(Tag=1)

    (g_x+=0)

    如果在这个点打了标记

    这个点和它的子树到根的路径上都一定有(Tag=1)

    这些节点的(g+=2^{t-1})

    剩下的节点到根的路径上的(Tag)情况不会改变

    (g_x*=2)

    考虑用线段树模拟维护(f)(g)

    因为涉及未访问节点

    可以不修改这些节点

    把访问的节点的值除(2)

    算答案的时候乘上(2^t)

    但这样(g)的修改不是很好维护

    考虑每次修改的同时都会将其除(2)

    所以用一个(tag)记录被修改了几次

    pushdown时将(g_{son})除去(2^{tag_x})

    然后加上(frac{1}{2},frac{1}{4},...,frac{1}{2^{tag_x}}=1-2^{tag_x})

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define gc c=getchar()
    #define r(x) read(x)
    #define ll long long
    
    template<typename T>
    inline void read(T&x){
        T k=1;char gc;x=0;
        while(!isdigit(c)){if(c=='-')k=-1;gc;}
        while(isdigit(c))x=x*10+c-'0',gc;x*=k;
    }
    
    const int N=1e5+7;
    const int p=998244353;
    const ll Inv=(p+1)>>1;
    
    int Pow[N];
    int sum=0,prod=1;
    
    inline int add(int a,int b){
        return (a+=b)>=p?a-p:a;
    }
    
    inline int sub(int a,int b){
        return (a-=b)<0?a+p:a;
    }
    
    int f[N<<2],g[N<<2],tag[N<<2];
    
    #define ls (rt<<1)
    #define rs (rt<<1|1)
    
    inline void pushdown(int rt){
        if(tag[rt]){
            g[ls]=add((ll)g[ls]*Pow[tag[rt]]%p,sub(1,Pow[tag[rt]]));
            g[rs]=add((ll)g[rs]*Pow[tag[rt]]%p,sub(1,Pow[tag[rt]]));
            tag[ls]+=tag[rt];
            tag[rs]+=tag[rt];
            tag[rt]=0;
        }
    }
    
    void modify(int rt,int l,int r,int x,int y){
        sum=sub(sum,f[rt]);
        f[rt]=f[rt]*Inv%p;
        g[rt]=g[rt]*Inv%p;
        if(x<=l&&r<=y){
            f[rt]=add(f[rt],Inv);
            g[rt]=add(g[rt],Inv);
        }
        else if(l>y||r<x){
            f[rt]=add(f[rt],g[rt]);
            g[rt]=add(g[rt],g[rt]);
        }
        sum=add(sum,f[rt]);
        if(l>y||r<x)return ;
        if(x<=l&&r<=y){
            ++tag[rt];
            return ;
        }
        pushdown(rt);
        int mid=(l+r)>>1;
        modify(ls,l,mid,x,y);
        modify(rs,mid+1,r,x,y);
    }
    
    int main(){
        freopen("segment.in","r",stdin);
        freopen("segment.out","w",stdout);
        int n,m;r(n),r(m);
        Pow[0]=1;
        for(int i=1;i<=m;++i){
            Pow[i]=Pow[i-1]*Inv%p;
        }
        while(m--){
            int opt,l,r;r(opt);
            if(opt==1){
                r(l),r(r);
                modify(1,1,n,l,r);
                prod=add(prod,prod);
            }
            else{
                printf("%lld
    ",(ll)sum*prod%p);
            }
        }
    }
    
  • 相关阅读:
    Git 自救指南:这些坑你都跳得出吗?
    敢不敢模拟超过 5 万的并发用户?
    一条简单的 SQL 执行超过 1000ms,纳尼?
    JVM 最多支持多少个线程?
    19 条效率至少提高 3 倍的 MySQL 技巧
    LeetCode 剑指offer 面试题04. 二维数组中的查找
    LeetCode 剑指offer 面试题03 数组中重复的数字
    东华大学计算机软件工程 复试最后一百题
    东华大学计算机软件工程复试 挑战练习
    东华大学计算机软件工程复试 进阶练习
  • 原文地址:https://www.cnblogs.com/yicongli/p/10643433.html
Copyright © 2011-2022 走看看