#线段树#洛谷 4340 [SHOI2016]随机序列

题目


分析

可以发现加号和减号会抵消掉,真正有用的答案就是第一段的乘积。

那也就是 \(\sum_{i=1}^nS_i*2*3^{n-i-1}\),其中 \(S_i\) 表示 \(a_1\)\(a_i\) 的乘积。

然后乘号后面可以选加号或者减号,而且最后一个位置要特判,单点修改逆元可能不存在,所以直接丢线段树上就可以了。

就是维护区间乘积的同时维护上面的答案即可


代码

#include <cstdio>
#include <cctype>
using namespace std;
const int N=100011,mod=1000000007;
int n,a[N],w[N<<2],s[N<<2],pw[N],Q;
int iut(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48); 
}
int mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
void build(int k,int l,int r){
	if (l==r){
		s[k]=a[l];
		if (l==n) w[k]=a[l];
		    else w[k]=2ll*a[l]*pw[n-l-1]%mod;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	s[k]=1ll*s[k<<1]*s[k<<1|1]%mod;
	w[k]=mo(w[k<<1],1ll*s[k<<1]*w[k<<1|1]%mod);
}
void update(int k,int l,int r,int x){
	if (l==r){
		s[k]=a[l];
		if (l==n) w[k]=a[l];
		    else w[k]=2ll*a[l]*pw[n-l-1]%mod;
		return;
	}
	int mid=(l+r)>>1;
	if (x<=mid) update(k<<1,l,mid,x);
	    else update(k<<1|1,mid+1,r,x);
	s[k]=1ll*s[k<<1]*s[k<<1|1]%mod;
	w[k]=mo(w[k<<1],1ll*s[k<<1]*w[k<<1|1]%mod);
}
int main(){
	n=iut(),Q=iut(),pw[0]=1;
	for (int i=1;i<=n;++i) a[i]=iut(),pw[i]=3ll*pw[i-1]%mod;
	build(1,1,n);
	for (int i=1;i<=Q;++i){
		int x=iut(),y=iut();
		a[x]=y,update(1,1,n,x);
		print(w[1]),putchar(10);
	} 
	return 0;
}
posted @ 2022-03-12 21:36  lemondinosaur  阅读(22)  评论(0编辑  收藏  举报