洛谷 2023 [AHOI2009]维护序列
这个题也是一道经典的线段树模版(其实洛谷的模版二改一下输入顺序就能AC),其中包括区间乘法修改、区间加法修改、区间查询三个操作。
线段树的基本操作就不再赘述了,建树,查询,修改,都比较简单,我们可以为两种操作的懒惰标记申请两个变量来记录,这道题的主要难点是down操作和两种修改的优先级问题。
这两个问题其实就是一回事:首先当我们下推标记时,如果该点加法标记不为0且乘法标记不为1(乘法标记初始化为1),那我们应该先推哪个标记呢?
实际上我们应该先推位置靠前(也就是比较早输入)的运算,可是运用懒惰标记后,我们是难以判断那个运算输入的更早。
有一种解决方法就是在每次乘法修改时,将对应位置的加法懒惰标记乘以修改值。这样我们保证了每次下推标记时必然是乘法优先。
if(tree[u].l>=x&&tree[u].r<=y) { tree[u].w*=c; tree[u].w%=mod; tree[u].mu=(tree[u].mu*c)%mod; tree[u].ad=(tree[u].ad*c)%mod; return; }
这里的模运算的性质:
((a*b) % p * c)% p = (a * (b*c) % p) % p
(a * b) % p = (b * a) % p
我们可以随时对程序里的标记和值做模运算。
而下推标记时,我们只要保证乘法优先,就可以轻松打出代码:
void down(int u) { tree[u<<1].w=(tree[u<<1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1].r-tree[u<<1].l+1)%mod)%mod; tree[u<<1|1].w=(tree[u<<1|1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1|1].r-tree[u<<1|1].l+1)%mod)%mod; tree[u<<1].mu=tree[u].mu*tree[u<<1].mu%mod; tree[u<<1|1].mu=tree[u].mu*tree[u<<1|1].mu%mod; tree[u<<1].ad=(tree[u].ad+tree[u<<1].ad*tree[u].mu)%mod; //下推时加法标记也要做乘法运算 tree[u<<1|1].ad=(tree[u].ad+tree[u<<1|1].ad*tree[u].mu)%mod; tree[u].ad=0; //清空标记 tree[u].mu=1; }
下附完整AC代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=100010; typedef long long ll; struct segtree{ int l; int r; ll w; ll ad; ll mu; }tree[MAXN<<2]; int n,m,q,x,y; ll mod,c,ans; ll read() { ll x=0; int k=1; char c=getchar(); while(c>'9'||c<'0') { if(c=='-') k=-1; c=getchar(); } while(c<='9'&&c>='0') x=x*10+c-'0', c=getchar(); return k*x; } void build(int u,int l,int r) { tree[u].l=l; tree[u].r=r; tree[u].ad=0; tree[u].mu=1; if(l==r) { tree[u].w=read()%mod; return; } int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); tree[u].w=(tree[u<<1].w+tree[u<<1|1].w)%mod; return; } void down(int u) { tree[u<<1].w=(tree[u<<1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1].r-tree[u<<1].l+1)%mod)%mod; tree[u<<1|1].w=(tree[u<<1|1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1|1].r-tree[u<<1|1].l+1)%mod)%mod; tree[u<<1].mu=tree[u].mu*tree[u<<1].mu%mod; tree[u<<1|1].mu=tree[u].mu*tree[u<<1|1].mu%mod; tree[u<<1].ad=(tree[u].ad+tree[u<<1].ad*tree[u].mu)%mod; tree[u<<1|1].ad=(tree[u].ad+tree[u<<1|1].ad*tree[u].mu)%mod; tree[u].ad=0; tree[u].mu=1; } void query(int u) { if(tree[u].l>=x&&tree[u].r<=y) { ans=(ans+tree[u].w)%mod; return; } down(u); int mid=(tree[u].l+tree[u].r)>>1; if(x<=mid) query(u<<1); if(y>mid) query(u<<1|1); return; } void mul(int u) { if(tree[u].l>=x&&tree[u].r<=y) { tree[u].w*=c; tree[u].w%=mod; tree[u].mu=(tree[u].mu*c)%mod; tree[u].ad=(tree[u].ad*c)%mod; return; } down(u); int mid=(tree[u].l+tree[u].r)>>1; if(x<=mid) mul(u<<1); if(y>mid) mul(u<<1|1); tree[u].w=(tree[u<<1].w+tree[u<<1|1].w)%mod; return; } void add(int u) { if(tree[u].l>=x&&tree[u].r<=y) { tree[u].ad=(tree[u].ad+c)%mod; tree[u].w+=(tree[u].r-tree[u].l+1)*c; tree[u].w%=mod; return; } down(u); int mid=(tree[u].l+tree[u].r)>>1; if(x<=mid) add(u<<1); if(y>mid) add(u<<1|1); tree[u].w=(tree[u<<1].w+tree[u<<1|1].w)%mod; return; } int main() { n=read(); mod=read(); build(1,1,n); m=read(); for(int i=1;i<=m;++i) { q=read(); x=read(); y=read(); if(q==1) { c=read(); mul(1); } if(q==2) { c=read(); add(1); } if(q==3) { ans=0; query(1); printf("%lld ",ans%mod); } }
return 0; }