SHOI2016 随机序列
给你一个数列,在相邻两个数之间插入加号,减号或乘号
每次支持单点修改,求所有这样可以得到的表达式之和,膜1e9 + 7
sol:
我是个 sb 。。。
可以发现,如果某位置出现了加号,后面一定有一个减号把它消掉,于是答案就是一些出现了好几次的前缀积之和
算一下每段前缀积的贡献即可
#include<bits/stdc++.h> #define int long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } int n,q; const int mod = 1e9 + 7,maxn = 1e5 + 10; int a[maxn],fac[maxn]; #define ls (x << 1) #define rs ((x << 1) | 1) int seg[maxn << 2],tag[maxn << 2]; inline int pw(int x,int t) { int res = 1;x %= mod; while(t) { if(t & 1)res = res * x % mod; x = x * x % mod; t = t >> 1; } return res; } inline void build(int x,int l,int r) { tag[x] = 1; if(l == r)seg[x] = fac[l]; else { int mid = (l + r) >> 1; build(ls,l,mid);build(rs,mid + 1,r); seg[x] = (seg[ls] + seg[rs]) % mod; } } inline void pushdown(int x,int l,int r) { if(tag[x] != 1) { (tag[ls] *= tag[x]) %= mod;(tag[rs] *= tag[x]) %= mod; (seg[ls] *= tag[x]) %= mod;(seg[rs] *= tag[x]) %= mod; tag[x] = 1; } } inline void update(int x,int l,int r,int L,int R,int val) { if(L <= l && r <= R) { (seg[x] *= val) %= mod; (tag[x] *= val) %= mod; return; } pushdown(x,l,r); int mid = (l + r) >> 1; if(L <= mid)update(ls,l,mid,L,R,val); if(R > mid)update(rs,mid + 1,r,L,R,val); seg[x] = (seg[ls] + seg[rs]) % mod; } signed main() { n = read(),q = read(); for(int i=1;i<=n;i++)a[i] = read(); int mul = 1; for(int i=1;i<=n;i++) { mul = (long long)mul * a[i] % mod; if(i == n)fac[i] = mul; else fac[i] = (long long)mul * 2 * pw(3, n - i - 1) % mod; }build(1,1,n); while(q--) { int p = read(),v = read(); update(1,1,n,p,n,(long long)v * pw(a[p],mod - 2) % mod); a[p] = v; printf("%lld\n",seg[1]); } }
$$\sum_{i=1}^{n-1}sum_i \times 2 \times 3^{n-i-1} + sum_n$$
$sum$ 数组为前缀积