zoukankan      html  css  js  c++  java
  • BZOJ4597:[SHOI2016]随机序列——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4597

    你的面前有N个数排成一行。分别为A1, A2, … , An。你打算在每相邻的两个 Ai和 Ai+1 间都插入一个加号或者减号或者乘号。那么一共有 3^(n-1) 种可能的表达式。你对所有可能的表达式的值的和非常感兴趣。但这毕竟太简单了,所以你还打算支持一个修改操作,可以修改某个Ai 的值。你能够编写一个程序对每个修改都输出修改完之后所有可能表达式的和吗?注意,修改是永久的,也就是说每次修改都是在上一次修改的基础上进行, 而不是在最初的表达式上进行。

    (讲个事为了防止我忘了这题怎么做从而写不了题解所以这题解是我边想题边写的233所以可能啰嗦些)

    首先我们肯定不可能暴力求和,那么我们打打表看看会怎么样。

    当n=2时为2*a1+a1*a2,当n=3时为6*a1+2*a1*a2+a1*a2*a3,当n=4时18*a1+6*a1*a2+2*a1*a2*a3+a1*a2*a3*a4……

    我们发现每次的系数都为后一项*3,然而将这个数列放到oeis上查能查到好多,这个结论不一定可靠,于是试图证明它(当然你可以跳过证明,毕竟不严格)。

    证明:我们只看a1项系数,于是放乘号的位置只有2^(n-2)个,并且设放乘号个数为i,则乘号合并后的数字显然只有(n-i)个。

    此时我们再往合并后的数字里面填符号的话,只有+/-的情况下显然=2^(n-i-1)*a1(剩余的项都被消了)

    于是我们得到了a1的个数:sigma(C(n-2,i)*2^(n-i-1))(0<=i<=n-2)。

    这个式子很像二项式定理,于是除2再乘2得到2*(2+1)^(n-2)=2*3^(n-2)。

    第一项我们证明出来了,那么第二项,第三项……都可以直接推出来,证毕。

    那么直接线段树维护即可,懒得说怎么维护了。

    #include<map>
    #include<cmath>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<vector>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    const int p=1e9+7;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    int qpow(int k,int n){
        int res=1;
        while(n){
        if(n&1)res=(ll)res*k%p;
        k=(ll)k*k%p;n>>=1;
        }
        return res;
    }
    int n,m,c[N],tr[N*4],b[N],q[N],lz[N*4];
    inline int add(int x,int y){
        x+=y;if(x>=p)x-=p;return x;
    }
    inline int mul(ll x,int y){
        return x*y%p;
    }
    inline void push(int a){
        if(lz[a]!=-1){
        tr[a<<1]=mul(tr[a<<1],lz[a]);
        tr[a<<1|1]=mul(tr[a<<1|1],lz[a]);
        if(lz[a<<1]==-1)lz[a<<1]=lz[a];
        else lz[a<<1]=mul(lz[a<<1],lz[a]);
        if(lz[a<<1|1]==-1)lz[a<<1|1]=lz[a];
        else lz[a<<1|1]=mul(lz[a<<1|1],lz[a]);
        lz[a]=-1;
        }
    }
    void build(int a,int l,int r){
        lz[a]=-1;
        if(l==r){
        tr[a]=mul(q[l],b[l]);return;
        }
        int mid=(l+r)>>1;
        build(a<<1,l,mid);build(a<<1|1,mid+1,r);
        tr[a]=add(tr[a<<1],tr[a<<1|1]);
    }
    void modify(int a,int l,int r,int l1,int r1,int w){
        if(r<l1||r1<l)return;
        if(l1<=l&&r<=r1){
        if(lz[a]==-1)lz[a]=w;
        else lz[a]=mul(lz[a],w);
        tr[a]=mul(tr[a],w);
        return;
        }
        push(a);
        int mid=(l+r)>>1;
        modify(a<<1,l,mid,l1,r1,w);modify(a<<1|1,mid+1,r,l1,r1,w);
        tr[a]=add(tr[a<<1],tr[a<<1|1]);
    }
    int main(){
        n=read(),m=read();b[0]=1;
        for(int i=1;i<=n;i++){
        c[i]=read();
        b[i]=mul(b[i-1],c[i]);
        }
        q[n]=1,q[n-1]=2;
        for(int i=n-2;i>=0;i--)q[i]=mul(q[i+1],3);
        build(1,1,n);
        while(m--){
        int k=read(),v=read();
        modify(1,1,n,k,n,(ll)v*qpow(c[k],p-2)%p);
        c[k]=v;
        printf("%d
    ",tr[1]);
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    HTTP网页错误代码大全带解释
    记录一下手把手教您做电商网站
    C#中的Attribute
    C#中dynamic的正确用法
    【CSP】最大的矩形
    【CSP】字符与int
    C++数组初始化
    C++中输出字符到文本文档
    C++ 中时钟函数的使用
    各种函数的头文件
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9188451.html
Copyright © 2011-2022 走看看