您的位置:首页 > 其它

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

2017-09-29 19:21 369 查看

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

为什么?(还需要解释吗)
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
,a
,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岁了,成为一位长者
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: