BZOJ-1798 [Ahoi2009]Seq 维护序列seq(双标记线段树)
题目描述
给定长为 \(n(1\leq n\leq 10^5)\) 的序列 \(a\) 和 \(m(1\leq m\leq 10^5)\) 次操作,有三种操作:
操作 \(1\):1 l r v,把区间 \([l,r]\) 中的 \(a_i\) 修改为 \(a_i\times v\)(\(0\leq v\leq 10^9\))。
操作 \(2\):2 l r v,把区间 \([l,r]\) 中的 \(a_i\) 修改为 \(a_i+v\)($0\leq v\leq 10^9 $)。
操作 \(3\):3 l r,求 \(\displaystyle\sum_{i=l}^{r}a_i\),答案对 \(p(1\leq p\leq 10^9)\) 取模。
分析
区间加法
tree[p].add=tree[p].add+v;
tree[p].sum=tree[p].sum+v*(r-l+1);
区间乘法
因为 \((ax+b)\times c+d=acx+bc+d\),这说明加法标记不会影响乘法标记,但是下放乘法标记必须把加法标记也乘一下,所以要先乘后加:在做乘法的时候把加法标记也乘上这个数,在后面做加法的时候直接加即可。
tree[p].add=(tree[p].add*v)%mod;
tree[p].mul=(tree[p].mul*v)%mod;
tree[p].sum=(tree[p].sum*v)%mod;
pushdown的维护
\(\text{mul}\):直接乘。
\(\text{add}\):因为 \(\text{add}\) 的值要包括乘之后的值,所以子节点需要先乘上 \(\text{mul}\)。
void pushdown(long long p)
{
long long mid=(tree[p].l+tree[p].r)>>1;
if(tree[p].mul!=1)
{
tree[p*2].mul=tree[p*2].mul*tree[p].mul%mod;
tree[p*2+1].mul=tree[p*2+1].mul*tree[p].mul%mod;
tree[p*2].add=tree[p*2].add*tree[p].mul%mod;
tree[p*2+1].add=tree[p*2+1].add*tree[p].mul%mod;
tree[p*2].sum=tree[p*2].sum*tree[p].mul%mod;
tree[p*2+1].sum=tree[p*2+1].sum*tree[p].mul%mod;
tree[p].mul=1;
}
if(tree[p].add!=0)
{
tree[p*2].add=(tree[p*2].add+tree[p].add)%mod;
tree[p*2+1].add=(tree[p*2+1].add+tree[p].add)%mod;
tree[p*2].sum=(tree[p*2].sum+tree[p].add*(mid-tree[p].l+1)%mod)%mod;
tree[p*2+1].sum=(tree[p*2+1].sum+tree[p].add*(tree[p].r-mid)%mod)%mod;
tree[p].add=0;
}
}
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,mod;
long long a[N];
struct SegmentTree
{
int l,r;
long long add,mul,sum;
}tree[N<<2];
void build(int p,int l,int r)
{
tree[p].l=l;tree[p].r=r;
tree[p].sum=tree[p].add=0;
tree[p].mul=1;
if(l==r)
{
tree[p].sum=a[l];
tree[p].mul=1;
return ;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
}
void pushdown(long long p)
{
if(tree[p].mul!=1)
{
tree[p*2].mul=tree[p*2].mul*tree[p].mul%mod;
tree[p*2+1].mul=tree[p*2+1].mul*tree[p].mul%mod;
tree[p*2].add=tree[p*2].add*tree[p].mul%mod;
tree[p*2+1].add=tree[p*2+1].add*tree[p].mul%mod;
tree[p*2].sum=tree[p*2].sum*tree[p].mul%mod;
tree[p*2+1].sum=tree[p*2+1].sum*tree[p].mul%mod;
tree[p].mul=1;
}
if(tree[p].add!=0)
{
tree[p*2].sum=(tree[p*2].sum+tree[p].add*(tree[p*2].r-tree[p*2].l+1))%mod;
tree[p*2+1].sum=(tree[p*2+1].sum+tree[p].add*(tree[p*2+1].r-tree[p*2+1].l+1))%mod;
tree[p*2].add=(tree[p*2].add+tree[p].add)%mod;
tree[p*2+1].add=(tree[p*2+1].add+tree[p].add)%mod;
tree[p].add=0;
}
}
void update_mul(int p,int l,int r,long long v)
{
if(l<=tree[p].l&&tree[p].r<=r)
{
tree[p].sum=tree[p].sum*v%mod;
tree[p].add=tree[p].add*v%mod;
tree[p].mul=tree[p].mul*v%mod;
return ;
}
pushdown(p);
int mid=(tree[p].l+tree[p].r)/2;
if(l<=mid)
update_mul(p*2,l,r,v);
if(r>mid)
update_mul(p*2+1,l,r,v);
tree[p].sum=(tree[p*2].sum+tree[p*2+1].sum)%mod;
}
void update_add(int p,int l,int r,long long v)
{
if(l<=tree[p].l&&tree[p].r<=r)
{
tree[p].sum=(tree[p].sum+(tree[p].r-tree[p].l+1)*v%mod)%mod;
tree[p].add=(tree[p].add+v)%mod;
return ;
}
pushdown(p);
int mid=(tree[p].l+tree[p].r)/2;
if(l<=mid)
update_add(p*2,l,r,v);
if(r>mid)
update_add(p*2+1,l,r,v);
tree[p].sum=(tree[p*2].sum+tree[p*2+1].sum)%mod;
}
long long query(int p,int l,int r)
{
if(l<=tree[p].l&&tree[p].r<=r)
return tree[p].sum;
pushdown(p);
int mid=(tree[p].l+tree[p].r)/2;
long long ans=0;
if(l<=mid)
ans=(ans+query(p*2,l,r))%mod;
if(r>mid)
ans=(ans+query(p*2+1,l,r))%mod;
return ans;
}
int main()
{
cin>>n>>mod;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n);
cin>>m;
while(m--)
{
int op;
scanf("%d",&op);
int l,r;
long long v;
if(op==1)
{
scanf("%d %d %lld",&l,&r,&v);
update_mul(1,l,r,v);
}
if(op==2)
{
scanf("%d %d %lld",&l,&r,&v);
update_add(1,l,r,v);
}
if(op==3)
{
scanf("%d %d",&l,&r);
printf("%lld\n",query(1,l,r));
}
}
return 0;
}
posted on 2020-11-25 21:17 DestinHistoire 阅读(100) 评论(0) 收藏 举报
浙公网安备 33010602011771号