2020NYIST个人积分赛第四场(线段树+前缀 后缀乘积和)

题意:

给n个位置,q次操作,每次对操作可以改变i位置的数,定义 f ( i , j ) = a i ∗ a ( i + 1 ) ∗ . . . ∗ a j . f(i,j) = ai * a(i+1) * ... * aj. f(i,j)=aia(i+1)...aj. 求整个区间中所有子区间乘积的和对10007取模

题解

主要难点是找到求所有子区间乘积和的规律,然后用线段树维护。通过找规律,可以发现,所求所有子区间的和为该节点左儿子的和+该节点右儿子的和+左儿子的后缀乘积和*右儿子的前缀乘积和。

开一个结构体,每个节点带四个信息:

struct node
{
int mul; 区间乘积
int qs;  前缀和
int hs;  后缀和
int sum; 区间和
}

我们可以通过区间乘积来维护区间前后缀的乘积和

维护代码:

void pushup(ll k)
{
   tr[k].mul=(tr[k<<1].mul*tr[k<<1|1].mul)%mod;
   tr[k].sum=((tr[k<<1].sum+tr[k<<1|1].sum)%mod+(tr[k<<1].hs*tr[k<<1|1].qs)%mod)%mod;
   tr[k].qs=(tr[k<<1].qs+(tr[k<<1].mul*tr[k<<1|1].qs)%mod)%mod;
   tr[k].hs=(tr[k<<1|1].hs+(tr[k<<1|1].mul*tr[k<<1].hs%mod))%mod;
}

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e6+5;
const ll mod=10007;
struct node
{
    ll mul,qs,hs,sum;
}tr[maxn<<2];
void pushup(ll k)
{
   tr[k].mul=(tr[k<<1].mul*tr[k<<1|1].mul)%mod;
   tr[k].sum=((tr[k<<1].sum+tr[k<<1|1].sum)%mod+(tr[k<<1].hs*tr[k<<1|1].qs)%mod)%mod;
   tr[k].qs=(tr[k<<1].qs+(tr[k<<1].mul*tr[k<<1|1].qs)%mod)%mod;
   tr[k].hs=(tr[k<<1|1].hs+(tr[k<<1|1].mul*tr[k<<1].hs%mod))%mod;
}
void inser(ll k,ll l,ll r,ll pos,ll w)
{
    if(l==r)
    {
        tr[k].sum=tr[k].mul=tr[k].qs=tr[k].hs=w%mod;
        return ;
    }
    ll mid=l+r>>1;
    if(mid>=pos)
        inser(k<<1,l,mid,pos,w);
    else
        inser(k<<1|1,mid+1,r,pos,w);
    pushup(k);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    ll n,q;
    cin>>n>>q;
    while(q--)
    {
        ll pos,w;
        cin>>pos>>w;
        inser(1,1,n,pos,w);
        cout<<(tr[1].sum%mod)<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值