zoukankan      html  css  js  c++  java
  • Luogu P4247 [清华集训]序列操作

    火焰之地传送门 

    祭第五道黑题

    叫序列操作的题怎么都这么恶心

    coding30min,debug三小时,线段树你值得拥有


    太久不写博客了,本来想集训完回家再一起写,但是de了线段树的bug真的敲不动主席树了,写(水)篇题解吧。

    题意如下:

    给定一个长度为n的序列。

    有m次操作,操作分为:

    1.区间加;

    2.区间取负;

    3.询问区间中选c个数乘积的和。

    n,m<=50000,c<=10。

    题目来源:bzoj2962

    区间操作问题,很容易想到用线段树。


    先放一下闫神讲的极简做法:(不是做法极简是说的极简)

    维护区间选0~c个数的答案。

    合并时枚举两边各选几个数。

    区间加和区间取反都可以打标记。

    时间复杂度O(mc2logn)
    orz


    反正我没完全懂,于是把这做法具体一点讲:

    在题解里看到一篇讲了写线段树要思考的五个问题,觉得很好理解,这里借鉴一下思路来讲:

    1.考虑每个区间维护哪些值?

    构成线段树的基本元素之一,显然要考虑:

    一般来说,维护的值,取决于题目询问的是什么。对于这题来说,就是维护每个区间选0~c(20)个数的答案。

    2.需要添加哪些标记?

    取决于会涉及如何修改树中的数据,加用常规的懒标记实现即可,区间取负可以直接标记。

    3.标记如何叠加?

    同区间若取了两次负,视为不取。

    关于优先级,其实这道题哪个标记优先都可以写,本蒟蒻为了方便定义取负优先。

    4.如何整体修改区间?

    (1):加法:假设区间  an—am +x

            计算贡献为:(an+x)(a(n+1)+x)...(am+x)

                             =(an*a(n+1)+x*(an+a(n+1))+x^2)....(am+x)

                             =an*a(n+1)*a(n+2)*...*am+x*(an+a(n+1)+...+a(m-1))+...+x^n

            找到规律了吧:结果为 sigma (j=0~i) x^(i-j)·随机 j 个a

            不怎么方便,考虑一下优化。

    右眼观察(滑稽)发现,特定的随机的j个a在计算过程中会被多次求值,因此只要直接计算它被包含了多少次就行了。

    什么情况下这j个a会被选呢?从(p-j)个数中任意选出(i-j)个数的时候啊,因为这时,显然已经有j个数确定了,这时能选择多少种,最有多少种情况包括了这j个a。

    so:ans[i] = sigma(j=0~i) x^(i-j)·ans[j]·C(p-j,i-j)

    预处理组合数就行咯。

    (2)取负:上面的神仙推导您都看懂了那理解这个应该相当简单了:

    对于奇数个-1相乘,结果为-1,对于偶数个,结果是1,所以若考虑的是区间内奇数个元素,则取负,偶数个直接跳过,它好我也好。

    5.区间如何合并?

    ans[now][i]= sigma (j=0~i) ans[ls][j] * ans[rs][i-j]

    毕竟是黑题,再附一些要注意的点:

    1.处理加操作时,ans要从小到大维护。

    2.选数时,要特判选0个时直接return。

    上代码吧,这码量很线段树,很序列操作:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    #define gc c=getchar()
    #define r(x) read(x)
    #define ll long long
    #define ls (rt<<1)
    #define rs (rt<<1|1)
    
    template<typename T>
    inline void read(T&x){
        x=0;T k=1;char gc;
        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=19940417;
    
    ll C[N][21];
    
    struct Seg
    {
        bool tag1;
        ll tag2,a[21];
        int siz;
        
        Seg()
    {
            tag1=0;
            tag2=0;
            memset(a,0,sizeof(a));
            siz=1;
        }
        
        inline const ll& operator [](const int &x)const
    {
            return a[x];
        }
        
        inline ll& operator [](const int &x)
    {
            return a[x];
        }
        
        inline void reverse()
    {
            tag1^=1;
            if(tag2)tag2=p-tag2;
            for(int i=1;i<20;i+=2)if(a[i])a[i]=p-a[i];
        }
        
        inline void increase(int x)
    {
            (tag2+=x)%=p;
            for(int i=min(20,siz);i;--i)
    {
                ll Tmp=x;
                for(int j=i-1;j;--j,(Tmp*=x)%=p)
    {
                    (a[i]+=Tmp*a[j]%p*C[siz-j][i-j]%p)%=p;
                }
                (a[i]+=C[siz][i]*Tmp)%=p;
            }
        }
            
    }tr[N<<2];
    
    inline Seg operator + (const Seg &a,const Seg &b)
    {
        Seg ret;
        ret[0]=1;
        ret.siz=a.siz+b.siz;
        for(int i=1;i<=min(ret.siz,20);++i)
    {
            ret[i]=(a[i]+b[i])%p;
            for(int j=1;j<i;++j)
    {
                (ret[i]+=a[j]*b[i-j])%=p;
            }
        }
        return ret;
    }
    
    inline void update(int rt)
    {
        tr[rt]=tr[ls]+tr[rs];
    }
    
    inline void pushdown(int rt)
    {
        if(tr[rt].tag1)
    {
            tr[ls].reverse();
            tr[rs].reverse();
            tr[rt].tag1=0;
        }
        if(tr[rt].tag2){
            tr[ls].increase(tr[rt].tag2);
            tr[rs].increase(tr[rt].tag2);
            tr[rt].tag2=0;
        }
    }
    
    int a[N];
    
    void build(int rt,int l,int r)
    {
        if(l==r)
        {
            tr[rt][0]=1;
            tr[rt][1]=a[l];
            return ;
        }
        int mid=(l+r)>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        update(rt);
    }
    
    void reverse(int rt,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y){
            tr[rt].reverse();
            return;
        }
        pushdown(rt);
        int mid=(l+r)>>1;
        if(x<=mid)reverse(ls,l,mid,x,y);
        if(y>mid)reverse(rs,mid+1,r,x,y);
        update(rt);
    }
    
    void increase(int rt,int l,int r,int x,int y,int v)
    {
        if(x<=l&&r<=y){
            tr[rt].increase(v);
            return;
        }
        pushdown(rt);
        int mid=(l+r)>>1;
        if(x<=mid)increase(ls,l,mid,x,y,v);
        if(y>mid)increase(rs,mid+1,r,x,y,v);
        update(rt);
    }
    
    Seg query(int rt,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)return tr[rt];
        pushdown(rt);
        int mid=(l+r)>>1;
        if(y<=mid)return query(ls,l,mid,x,y);
        else if(x>mid)return query(rs,mid+1,r,x,y);
        else return query(ls,l,mid,x,y)+query(rs,mid+1,r,x,y);
    }
    
    inline void pre(int n)
    {
        for(int i=0;i<=n;++i
    {
            C[i][0]=C[i][i]=1;
            for(int j=1;j<=min(i-1,20);++j)
    {
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
            }
        }
    }
    
    int main()
    {
        int n,q;r(n),r(q);pre(n);
        for(int i=1;i<=n;++i)r(a[i]),a[i]=(a[i]%p+p)%p;
        build(1,1,n);
        while(q--)
    {
            char opt[5];scanf("%s",opt);
            int l,r,x;r(l),r(r);
            switch(opt[0])
    {
                case 'I':{r(x);x=(x%p+p)%p;increase(1,1,n,l,r,x);break;}
                case 'R':{reverse(1,1,n,l,r);break;}
                case 'Q':{r(x),printf("%lld
    ",query(1,1,n,l,r)[x]);break;}
            }
        }
    }

    滚回寝室睡觉了,不然又要“晚上不要睡,早上不要起,你反了你了!”

    祝各位黑题+1A

  • 相关阅读:
    jenkins:用jenkins通过ssh部署jar包到远程linux机器(jdk 15 / jenkins 2.257)
    linux(centos8):安装java jdk 15 (java 15)
    38. 外观数列 迭代
    58. 最后一个单词的长度
    kmp字符串匹配
    单词规律
    1502. 判断能否形成等差数列
    1496. 判断路径是否相交
    1475. 商品折扣后的最终价格
    一维数组的动态和
  • 原文地址:https://www.cnblogs.com/charlesss/p/10389313.html
Copyright © 2011-2022 走看看