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

题意:

给n个位置,q次操作,每次对操作可以改变i位置的数,定义 f ( i , j ) = a i a ( i + 1 ) . . . a j . f(i,j) = ai * a(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;
    }
}
发布了254 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yangzijiangac/article/details/105405529