P4340 [SHOI2016]随机序列(线段树)

结论:

只有前缀积有贡献。

因为第一个数前面是没有符号的,除了前缀积外,都可以通过加号变成减号、减号变加号使其贡献抵消。

接下来考虑每个前缀积的贡献。

我们可以枚举前缀积的位置 i i i

记录前缀积为 s i s_i si,第 i i i个符号可以有两种选择 + , − +,- +,

然后其他符号任意选。

所以一共有: 2 × 3 n − i − 1 2\times 3^{n-i-1} 2×3ni1

然后总和就是:

a n s = ∑ i = 1 n − 1 s i × 2 × 3 n − i − 1 + s n \large ans=\sum\limits_{i=1}^{n-1}s_i\times 2\times 3^{n-i-1}+s_n ans=i=1n1si×2×3ni1+sn

这里可以预处理每个 i i i对应的答案,然后修改 a i a_i ai就是相当于后缀区间乘法。

因为有模,所以要预处理逆元,但是 a i a_i ai非负, 0 0 0不存在逆元。所以此方法不可行。

考虑维护两个变量区间和 s s s 和区间 a i a_i ai的乘积 m m m

然后更新的话就是: a m = l s o n m + r s o n m , a s = l s o n s + r s o n s × l s o n m a_m=lson_m+rson_m,a_s=lson_s+rson_s\times lson_m am=lsonm+rsonm,as=lsons+rsons×lsonm

// Problem: P4340 [SHOI2016]随机序列
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4340
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// Date: 2021-08-18 17:17:09
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\n",a[n]); 
}
int n,q;
//区间修改 区间求和
#define il inline 
#define lx x<<1
#define rx x<<1|1
#define len(x) (a[x].r-a[x].l+1)
struct node{
	int l,r,lz;
	ll s,m;
}a[N<<2];
int p[N],w[N];
il void re(int x){
	a[x].m=a[lx].m*a[rx].m%mod;
	a[x].s=(a[lx].s+a[rx].s*a[lx].m%mod)%mod;
}
il void bud(int x,int l,int r){
	a[x].l=l,a[x].r=r;
	if(l==r){
		a[x].m=w[l];
		a[x].s=l<n?w[l]*2LL*p[n-l-1]%mod:w[n];
		return;
	}
	int m=(l+r)>>1;bud(lx,l,m),bud(rx,m+1,r);
	re(x);
}
il void upd(int x,int pos,int v){
	if(a[x].l==a[x].r){
		a[x].m=w[pos]=v;
		a[x].s=a[x].l<n?v*2LL*p[n-pos-1]%mod:v;return;
	}
	int m=a[x].l+a[x].r>>1;
	if(pos<=m) upd(lx,pos,v);
	else upd(rx,pos,v);
	re(x);
}
int main(){
	scanf("%d%d",&n,&q);p[0]=1;
	for(int i=1;i<=n;i++){
		scanf("%d",&w[i]);
		p[i]=p[i-1]*3LL%mod;
	}
	bud(1,1,n);
	while(q--){
		int p,v;scanf("%d%d",&p,&v);
		upd(1,p,v);
		printf("%lld\n",a[1].s);
	}
	return 0;
}