【bzoj4597】 [Shoi2016]随机序列

本文介绍了一种使用线段树和逆元处理数组乘积更新与查询的方法。通过预处理逆元并利用线段树维护数组元素的改变,可以高效地解决涉及大量更新与查询操作的问题。
可以发现加减号之间可以互相抵消.
真正加到答案里的只有一些前缀积.
记s[i]为a[1]*a[2]*a[3]...*a[i].那s[i]在答案中出现的次数就是2*3^(n-i-1);
修改一个数只会对后面的数有影响.
预处理逆元然后用线段树维护即可.
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
#define P 1000000007
using namespace std;
long long t[N<<2],inv[N],a[N],s[N],bin[N],x,p[N<<2];
int n,q,pos;
void build(int k,int l,int r){
  int mid=(l+r)>>1;
  if (l==r){
    if (l!=n) t[k]=(s[l]*bin[n-l-1]*2)%P;
    else t[k]=s[l];
    return; 
  }
  build(k<<1,l,mid);
  build(k<<1|1,mid+1,r);
  t[k]=(t[k<<1]+t[k<<1|1])%P;
}
void paint(int k,long long v){
  t[k]=(t[k]*v)%P;
  p[k]=(p[k]*v)%P;
}
void pushdown(int k,int l,int r){
   int mid=(l+r)>>1;
   paint(k<<1,p[k]);
   paint(k<<1|1,p[k]);
   p[k]=1;
}
void change(int k,int l,int r,int ll,int rr,long long v){
  int mid=(l+r)>>1;
  if (ll<=l&&r<=rr){paint(k,v);return;}
  if (p[k]!=1) pushdown(k,l,r);
  if (ll<=mid) change(k<<1,l,mid,ll,rr,v);
  if (mid<rr) change(k<<1|1,mid+1,r,ll,rr,v);
  t[k]=(t[k<<1]+t[k<<1|1])%P; 
}
int main(){
  scanf("%d%d",&n,&q);
  for (int i=1;i<=n;i++) scanf("%lld",&a[i]); 
  bin[0]=1;
  for (int i=1;i<=n;i++) bin[i]=(bin[i-1]*3)%P;
  inv[1]=1;
   for(int i=2;i<=10000;i++)
        inv[i]=(P-(long long)P/i*inv[P%i]%P);
  s[1]=a[1];
  for (int i=2;i<=n;i++) s[i]=(s[i-1]*a[i])%P;
  build(1,1,n);
  for (int i=1;i<=n*2;i++) p[i]=1;
  for (int i=1;i<=q;i++){  
    scanf("%d%lld",&pos,&x);
    change(1,1,n,pos,n,x*inv[a[pos]]%P);
    a[pos]=x;printf("%lld\n",t[1]);
  }
}

  

转载于:https://www.cnblogs.com/yangjiyuan/p/5519952.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值