传送门

这种题一般就慢慢推式子吧。。。

对一个数列,在任意添加符号时候你会发现,某个数字前面有多少种情况添加加号,就有多少种情况添加减号,就会相互抵消。而第一个数字前面加不了东西,不受这个规律影响。

所以经过一系列合并最后剩下的一定是若干个前缀乘(类比于前缀和)的和。

所以前缀乘组合情况是怎么样的呢?令$g(i)=\prod\limits_{i=1}^{n}a[i]$,即前缀乘。

假设数列有$5$个数字,为$a,b,c,d,e$。$g(5)=a*b*c*d*e$。显然$g(5)$只会出现$1$次。

把最后一个乘号替换为加号和减号,$a*b*c*d+e$和$a*b*c*d-e$就会相加相互抵消,得到$2$个$g(4)$。

再把$g(5)$和$g(4)$的每种情况的倒数第二个乘号替换为加号和减号,相加后就会得到$6$个$g(3)$。以此类推,有$18$个$g(2)$,$54$个$g(1)$。这样规律就比较明显了:每个$g(i)$都珂以由$g(k)(i<k\le n)$修改对应位置的乘号,得到$2$个$g(i)$。

定义函数$f(i)=\begin{cases}1&i=1\\\\\sum\limits_{j=1}^{i-1}f(j)*2&i>1\end{cases}$

答案就是 $ans=\sum\limits_{i=1}^{ n}g(i)*f(n-i+1)$

(其实这个规律完全可以爆搜用瞪眼法得出来)

$f(i)$和$g(i)$珂以直接预处理出来,然后直接上线段树维护,每个节点维护所代表区间的$\sum\limits_{i=l}^{r}g(i)*f(n-i+1)$。

对于修改:比如还是那五个数,要修改第三个数$c$为$k$。类似于前缀和,单点修改影响的是$g(3),g(4),g(5)$,也就是要把

$f(3)*a*b*c+f(2)*a*b*c*d+f(1)*a*b*c*d*e$

修改为

$f(3)*a*b*k+f(2)*a*b*k*d+f(1)*a*b*k*d*e$

就珂以把原式乘上$\frac{k}{c}$。有除法还要取模显然要用到乘法逆元,即为把带修点到$n$的区间乘上$k*inv(c)$,就是个线段树区间乘。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

#define maxn 100005
#define inf 0x3f3f3f3f

const long long mod = 1000000007;

using namespace std;

inline int read(){
    int x=0,y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
template<typename T>
inline T read(){
    T x=0;
    int y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
int n,a[maxn];
long long f[maxn],g[maxn],inv[maxn]={0,1};
struct Segment_Tree{
    long long dat[maxn<<2],tag[maxn<<2];
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
    inline void update(int node){
        dat[node]=(dat[ls(node)]+dat[rs(node)])%mod;
    }
    inline void datadown(int node,long long d){
        (dat[node]*=d)%=mod;
        (tag[node]*=d)%=mod;
    }
    inline void pushdown(int node){
        datadown(ls(node),tag[node]);
        datadown(rs(node),tag[node]);
        tag[node]=1;
    }
    void build(int l,int r,int node){
        tag[node]=1;
        if(l==r){
            dat[node]=g[l]*f[n-l+1]%mod;
            return;
        }
        int mid=l+r>>1;
        build(l,mid,ls(node));
        build(mid+1,r,rs(node));
        update(node);
    }
    void add(int L,int R,int l,int r,int node,long long d){
        if(L<=l&&R>=r){
            datadown(node,d);
            return;
        }
        if(tag[node]!=1)pushdown(node);
        int mid=l+r>>1;
        if(L<=mid)add(L,R,l,mid,ls(node),d);
        if(R>mid)add(L,R,mid+1,r,rs(node),d);
        update(node);
    }
}st;
int main(){
    for(register int i=2;i<=10000;++i)
        inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;//数据范围是允许预处理出逆元的
    n=read();
    int m=read();
    long long sum=1;
    f[1]=1,g[0]=1;
    for(register int i=2;i<=n;++i)
        f[i]=(sum<<1)%mod,(sum+=f[i])%=mod;//预处理f数组
    for(register int i=1;i<=n;++i)
        a[i]=read(),g[i]=g[i-1]*a[i]%mod;//预处理前缀乘
    st.build(1,n,1);
    while(m--){
        int x=read(),d=read();
        st.add(x,n,1,n,1,1ll*d*inv[a[x]]%mod);
        printf("%lld\n",st.dat[1]);
        a[x]=d;
    }
}