[bzoj4597][Shoi2016]随机序列 线段树

来源:互联网 发布:决战武林宠物升级数据 编辑:程序博客网 时间:2024/05/22 10:46

4597: [Shoi2016]随机序列

Time Limit: 20 Sec  Memory Limit: 256 MB
[Submit][Status][Discuss]

Description

你的面前有N个数排成一行。分别为A1, A2, … , An。你打算在每相邻的两个 Ai和 Ai+1 间都插入一个加号或者
减号或者乘号。那么一共有 3^(n-1) 种可能的表达式。你对所有可能的表达式的值的和非常感兴趣。但这毕竟太
简单了,所以你还打算支持一个修改操作,可以修改某个Ai 的值。你能够编写一个程序对每个修改都输出修改完
之后所有可能表达式的和吗?注意,修改是永久的,也就是说每次修改都是在上一次修改的基础上进行, 而不是
在最初的表达式上进行。

Input

第一行包含 2 个正整数 N 和 Q,为数的个数和询问的个数。
接下来一行 n 个非负整数,依次表示a1,a2...an
在接下来 Q 行,其中第 ?? 行两个非负整数Ti 和Vi,表示要将 Ati 修改为 Vi。其中 1 ≤ Ti ≤ N。
保证对于 1 ≤ J ≤ N, 1 ≤ i≤ Q,都有 Aj,Vi ≤ 10^4。
N,Q<=100000,本题仅有三组数据

Output

输出共 Q 行,其中第 i 行表示第 i 个询问之后所有可能表达式的和,对10^9 + 7 取模。

Sample Input

5 5
9384 887 2778 6916 7794
2 8336
5 493
3 1422
1 28
4 60

Sample Output

890543652
252923708
942282590
228728040
608998099

HINT

看来我被这道题骗了
一个等式
{前}+{后}
{前}-{后}
加起来
等于
2*{前}
于是此题就是维护一个值
fac[i]表示a[1]*a[2]*a[3]...a[i]
sigma 2*fac[i]*3^(n-i-1) + fac[n]
为什么?(还需要解释吗)
fac[i]仅存在于第i位是+或-号,前面均为*,+ - * 有(n-1)- i 位,就是3的那么多次方
所以区间乘法就完了
为甚么我的线段树常数下不来!!!
逆元用欧拉定理预处理(只有10000)
#include <bits/stdc++.h>using namespace std;const int N = 1000000 + 5;const int mod = 1e9 + 7;int inv[10005],n,q,fac[N],a[N],sum[N<<2],flag[N<<2];int quik_pow( int x, int p ){int ret = 1;while( p ){if( p & 1 ) ret = 1LL * ret * x % mod;x = 1LL * x * x % mod;p >>= 1;}return ret;}void pushdown( int k ){if( flag[k] != 1 ){flag[k<<1] = 1LL * flag[k<<1] * flag[k] % mod;flag[k<<1|1] = 1LL * flag[k<<1|1] * flag[k] % mod;sum[k<<1] = 1LL * sum[k<<1] * flag[k] % mod;sum[k<<1|1] = 1LL * sum[k<<1|1] * flag[k] % mod;flag[k] = 1;}}void update( int k ){sum[k] = ( sum[k<<1] + sum[k<<1|1] ) % mod;}void build( int k, int l, int r ){flag[k] = 1;if( l == r ){if( l == n ) sum[k] = fac[l];else sum[k] = 1LL * fac[l] * ( 2LL * quik_pow( 3, n - l - 1 ) % mod ) % mod;return ;}int mid = l + r >> 1;build( k<<1, l, mid );build( k<<1|1, mid+1, r );update( k );}void change( int k, int l, int r, int L, int R, int x ){if( l >= L && r <= R ){sum[k] = 1LL * sum[k] * x % mod;flag[k] = 1LL * flag[k] * x % mod;return ;}pushdown( k );int mid = l + r >> 1;if( mid >= L ) change( k<<1, l, mid, L, R, x );if( mid <  R ) change( k<<1|1, mid+1, r, L, R, x );update( k );}int main(){fac[0] = 1;for( int i = 1; i <= 10000; i++ ) inv[i] = quik_pow( i, mod-2 );scanf( "%d%d", &n, &q );for( int i = 1; i <= n; i++ ) scanf( "%d", &a[i] ), fac[i] = 1LL * fac[i-1] * a[i] % mod;build( 1, 1, n );while( q-- ){int x,y;scanf( "%d%d", &x, &y ); int yy= 1LL * y * inv[a[x]] % mod;change( 1, 1, n, x, n, yy ); a[x] = y;printf( "%d\n", sum[1] );}return 0;}
明天我就16岁了,成为一位长者

原创粉丝点击