这种题一般就慢慢推式子吧。。。
对一个数列,在任意添加符号时候你会发现,某个数字前面有多少种情况添加加号,就有多少种情况添加减号,就会相互抵消。而第一个数字前面加不了东西,不受这个规律影响。
所以经过一系列合并最后剩下的一定是若干个前缀乘(类比于前缀和)的和。
所以前缀乘组合情况是怎么样的呢?令$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;
}
}