题解
2025-10-04 16:12:33
发布于:广东
3阅读
0回复
0点赞
看到数据范围就知道肯定不能暴力做,操作对应的分别是区间乘,区间加和区间求和,很显然要利用带懒标记的线段树;
一个是乘法,一个是加法,用一个懒标记肯定是不够用的,所以可以新建两个懒标记:和,修改时应表现为的形式.
需要先更新,因为乘法优先级大于加法,所以先乘后加,再下传两个标记,也是先乘后加,运算过程中始终保持的形式
inline void pushdown(int l,int r,int id)
{
if (add[id]==0 && mul[id]==1) return; //没有更新,跳过
int mid=(l+r)>>1;
int lt=mid-l+1,rt=r-mid; //计算左区间和右区间长度
tree[id<<1]=(tree[id<<1]*mul[id]+add[id]*lt)%mod; //先乘mul,再加add
tree[id<<1|1]=(tree[id<<1|1]*mul[id]+add[id]*rt)%mod;
add[id<<1]=(add[id<<1]*mul[id]+add[id])%mod,add[id<<1|1]=(add[id<<1|1]*mul[id]+add[id])%mod;
mul[id<<1]=(mul[id<<1]*mul[id])%mod,mul[id<<1|1]=(mul[id<<1|1]*mul[id])%mod;
//下传也要保持这个顺序
mul[id]=1,add[id]=0; //清除懒标记
}
更新函数
先是乘法更新。前面说过,修改乘法时应表现为的形式,那如果是先加后乘,它对add有什么影响呢,计算一下试试看(为原值,为add标记,为mul标记):
可以发现,修改时不仅仅要把乘上,还要对进行相同的操作,
至于加法更新就不说了,更简单
inline void update_mul(int l,int r,int ql,int qr,int v,int id)
{
if (ql<=l && qr>=r)
{
tree[id]=(tree[id]*v)%mod,mul[id]=(mul[id]*v)%mod,add[id]=(add[id]*v)%mod;
//不仅要更新mul,还要更新add
return;
}
pushdown(l,r,id);
//下传懒标记
int mid=(l+r)>>1;
if (ql<=mid) update_mul(l,mid,ql,qr,v,id<<1);
if (qr>mid) update_mul(mid+1,r,ql,qr,v,id<<1|1);
tree[id]=(tree[id<<1]+tree[(id<<1)|1])%mod;
}
inline void update_add(int l,int r,int ql,int qr,int v,int id)
{
if (ql<=l && qr>=r)
{
tree[id]=(tree[id]+v*(r-l+1))%mod,add[id]=(add[id]+v)%mod;
return;
}
pushdown(l,r,id);
int mid=(l+r)>>1;
if (ql<=mid) update_add(l,mid,ql,qr,v,id<<1);
if (qr>mid) update_add(mid+1,r,ql,qr,v,id<<1|1);
tree[id]=(tree[id<<1]+tree[(id<<1)|1])%mod;
}
完整代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[100005],mod;
class segtree
{
private:
vector<int> tree,add,mul;
inline void build(int l,int r,int id,int arr[])
{
if (l==r)
{
tree[id]=arr[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,id<<1,arr);
build(mid+1,r,id<<1|1,arr);
tree[id]=(tree[id<<1]+tree[(id<<1)|1])%mod;
}
public:
segtree(int a[])
{
tree.resize((n<<2)+5),add.resize((n<<2)+5,0),mul.resize((n<<2)+5,1);
build(1,n,1,a);
}
inline void pushdown(int l,int r,int id)
{
if (add[id]==0 && mul[id]==1) return; //没有更新,跳过
int mid=(l+r)>>1;
int lt=mid-l+1,rt=r-mid; //计算左区间和右区间长度
tree[id<<1]=(tree[id<<1]*mul[id]+add[id]*lt)%mod; //先乘mul,再加add
tree[id<<1|1]=(tree[id<<1|1]*mul[id]+add[id]*rt)%mod;
add[id<<1]=(add[id<<1]*mul[id]+add[id])%mod,add[id<<1|1]=(add[id<<1|1]*mul[id]+add[id])%mod;
mul[id<<1]=(mul[id<<1]*mul[id])%mod,mul[id<<1|1]=(mul[id<<1|1]*mul[id])%mod;
//下传也要保持这个顺序
mul[id]=1,add[id]=0; //清除懒标记
}
inline void update_mul(int l,int r,int ql,int qr,int v,int id)
{
if (ql<=l && qr>=r)
{
tree[id]=(tree[id]*v)%mod,mul[id]=(mul[id]*v)%mod,add[id]=(add[id]*v)%mod;
//不仅要更新mul,还要更新add
return;
}
pushdown(l,r,id);
//下传懒标记
int mid=(l+r)>>1;
if (ql<=mid) update_mul(l,mid,ql,qr,v,id<<1);
if (qr>mid) update_mul(mid+1,r,ql,qr,v,id<<1|1);
tree[id]=(tree[id<<1]+tree[(id<<1)|1])%mod;
}
inline void update_add(int l,int r,int ql,int qr,int v,int id)
{
if (ql<=l && qr>=r)
{
tree[id]=(tree[id]+v*(r-l+1))%mod,add[id]=(add[id]+v)%mod;
return;
}
pushdown(l,r,id);
int mid=(l+r)>>1;
if (ql<=mid) update_add(l,mid,ql,qr,v,id<<1);
if (qr>mid) update_add(mid+1,r,ql,qr,v,id<<1|1);
tree[id]=(tree[id<<1]+tree[(id<<1)|1])%mod;
}
inline int query(int l,int r,int ql,int qr,int id)
{
if (ql<=l && qr>=r)
return tree[id];
pushdown(l,r,id);
int mid=(l+r)>>1,sum=0;
if (ql<=mid) sum+=query(l,mid,ql,qr,id<<1);
if (qr>mid) sum+=query(mid+1,r,ql,qr,id<<1|1);
return sum%mod;
}
};
signed main()
{
cin >> n >> mod;
for (int i=1;i<=n;i++)
cin >> a[i];
cin >> m;
segtree tree(a);
int q,x,y,k;
while (m--)
{
cin >> q >> x >> y;
if (q==1)
{
cin >> k;
tree.update_mul(1,n,x,y,k,1);
}
else if (q==2)
{
cin >> k;
tree.update_add(1,n,x,y,k,1);
}
else
cout << tree.query(1,n,x,y,1) << endl;
}
return 0;
}
这里空空如也





有帮助,赞一个