4597: [Shoi2016]随机序列

4597: [Shoi2016]随机序列

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 168   Solved: 115
[ 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,表示要将 A ti 修改为 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

Source

[ Submit][ Status][ Discuss]



题目描述得很难很难。。不过真正需要统计的东西其实很少

对于任意一种表达式,把乘号左右两边并起来,这样整个表达式被分为k个部分

除了第一部分,后面每部分对最终答案都没有贡献

因为这样的东西出现多少次+就会有多少个带-的式子和它抵消= =

也就是说,每个表达式对ans的贡献就是从第一个数字开始,到第一个不是*前面的数字的乘积

那么,有

单点修改的话影响后面一串,利用逆元消除之前的影响,区间修改即可

那当然是线段树维护了= =


一开始居然忘记在Modify操作里打pushdown。。。惨

可是,,,这题数据好弱啊。。。。题面里不是说非负数么!!

非负数啊,0哪里有逆元?????

但是bzoj只有三组数据。。三组,而且每一组里的数字全是正的。。

如果位置k的数字为0,那么k及其之后算出的结果都是0

可以不进行修改转而打标记

询问的话只要找到左数第一个非0位,把左边的答案拿出来就好了

用配对堆易于实现

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<cstring>  
#include<ext/pb_ds/priority_queue.hpp>  
using namespace std;  
  
const int maxn = 1E5 + 10;  
const int T = 4;  
typedef long long LL;  
const LL mo = 1000000007;  
typedef __gnu_pbds::priority_queue<int,greater<int>,__gnu_pbds::pairing_heap_tag> Heap;  
  
int n,m,A[maxn],F[maxn],mi[maxn],c[maxn*T],Mark[maxn*T];  
bool zeo[maxn];  
  
Heap Q;  
Heap::point_iterator id[maxn];  
  
int Mul(const LL &x,const LL &y) {return x*y%mo;}  
int Add(const LL &x,const LL &y) {return (x + y)%mo;}  
  
void pushdown(int o,int l,int r)  
{  
    if (Mark[o] == 1) return;  
    c[o] = Mul(c[o],Mark[o]);  
    if (l == r) {Mark[o] = 1; return;}  
    Mark[o<<1] = Mul(Mark[o<<1],Mark[o]);  
    Mark[o<<1|1] = Mul(Mark[o<<1|1],Mark[o]);  
    Mark[o] = 1;  
}  
  
void Build(int o,int l,int r)  
{  
    if (l == r)  
    {  
        c[o] = Mul(F[l],mi[l]);  
        Mark[o] = 1; return;  
    }     
    int mid = (l + r) >> 1; Mark[o] = 1;  
    Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);  
    c[o] = Add(c[o<<1],c[o<<1|1]);  
}  
  
void Modify(int o,int l,int r,int ml,int mr,int x)  
{  
    if (ml <= l && r <= mr)  
    {  
        Mark[o] = Mul(Mark[o],x);  
        pushdown(o,l,r); return;  
    }  
    int mid = (l + r) >> 1; pushdown(o,l,r);  
    if (ml <= mid) Modify(o<<1,l,mid,ml,mr,x); else pushdown(o<<1,l,mid);  
    if (mr > mid) Modify(o<<1|1,mid+1,r,ml,mr,x); else pushdown(o<<1|1,mid+1,r);  
    c[o] = Add(c[o<<1],c[o<<1|1]);  
}  
  
int ksm(int x,int y)  
{  
    int ret = 1;  
    for (; y; y >>= 1)  
    {  
        if (y&1) ret = Mul(ret,x);  
        x = Mul(x,x);  
    }  
    return ret;  
}  
  
int Query(int o,int l,int r,int ql,int qr)  
{  
	pushdown(o,l,r);
    if (ql <= l && r <= qr) return c[o];  
    int mid = (l + r) >> 1,ret = 0;  
    if (ql <= mid) ret = Add(ret,Query(o<<1,l,mid,ql,qr));  
    if (qr > mid) ret = Add(ret,Query(o<<1|1,mid+1,r,ql,qr));  
    return ret;  
}  
  
int main()  
{  
    #ifdef DMC  
        freopen("DMC.txt","r",stdin);  
    #endif  
      
    cin >> n >> m; F[0] = mi[n] = 1; mi[n-1] = 2;  
    id[n+1] = Q.push(n+1);  
    for (int i = 1; i <= n; i++)   
    {  
        scanf("%d",&A[i]);  
        if (!A[i]) id[i] = Q.push(i),A[i] = 1,zeo[i] = 1;  
        F[i] = Mul(A[i],F[i-1]);  
    }  
    for (int i = n - 2; i > 0; i--) mi[i] = Mul(mi[i+1],3);  
    Build(1,1,n);  
    while (m--)  
    {  
        int pos,x; scanf("%d%d",&pos,&x);  
        if (zeo[pos] && x)  
        {  
            Q.erase(id[pos]);  
            Modify(1,1,n,pos,n,Mul(ksm(A[pos],mo-2),x));  
            A[pos] = x; zeo[pos] = 0;  
        }  
        else if (!zeo[pos] && !x) id[pos] = Q.push(pos),zeo[pos] = 1;  
        else if (!zeo[pos] && x)  
        {     
            Modify(1,1,n,pos,n,Mul(ksm(A[pos],mo-2),x));  
            A[pos] = x;   
        }  
        int k = Q.top();  
        if (k > 1)  
            printf("%d\n",Query(1,1,n,1,k-1));  
        else puts("0");  
    }  
    return 0;  
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值