洛谷 P3373 【模板】线段树 2 题解

    xiaoxiao2023-09-26  150

    洛谷 P3373 【模板】线段树 2 题解

    题面题目链接:[戳这里](https://www.luogu.org/problemnew/show/P3373)题目描述输入输出格式输入输出样例说明 题解代码:

    题面

    题目链接:戳这里

    题目描述

    如题,已知一个数列,你需要进行下面三种操作:

    1.将某区间每一个数乘上x

    2.将某区间每一个数加上x

    3.求出某区间每一个数的和

    输入输出格式

    输入格式:

    第一行包含三个整数N、M、P,分别表示该数列数字的个数、操作的总个数和模数。

    第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

    接下来M行每行包含3或4个整数,表示一个操作,具体如下:

    操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k

    操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k

    操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果

    输出格式:

    输出包含若干行整数,即为所有操作3的结果。

    输入输出样例

    输入样例#1: 5 5 38 1 5 4 2 3 2 1 4 1 3 2 5 1 2 4 2 2 3 5 5 3 1 4 输出样例#1: 17 2

    说明

    时空限制:1000ms,128M

    数据规模:

    对于30%的数据:N<=8,M<=10

    对于70%的数据:N<=1000,M<=10000

    对于100%的数据:N<=100000,M<=100000

    (数据已经过加强_

    样例说明: 故输出应为17、2(40 mod 38=2)

    题解

    看起来不难的样子。。无非就是俩标记,按时取模,然后加的时候加到加法懒标记里,乘的时候加法懒标记和乘法懒标记都乘上。

    注意取模!!!注意取模!!!注意取模!!!

    代码:

    #include<cstdio> using namespace std; typedef long long ll; const ll maxn=100005; ll a[maxn],n,m,p; struct tree{ ll l,r,val,laza,lazm;//l,r当前节点左右边界,val区间和,laza加法懒标记,lazm乘法懒标记 void check(){ val%=p,laza%=p,lazm%=p; }//检查(取模) }tr[maxn*4]; void pushdown(ll id){//下传懒标记,第一次提交时忘记将子节点的加数乘上子节点的容量,于是果断爆零 tr[id].check(); tr[id<<1].val*=tr[id].lazm; tr[id<<1].val+=tr[id].laza*(tr[id<<1].r-tr[id<<1].l+1); tr[id<<1].lazm*=tr[id].lazm; tr[id<<1].laza=tr[id<<1].laza*tr[id].lazm+tr[id].laza; tr[id<<1|1].val*=tr[id].lazm; tr[id<<1|1].val+=tr[id].laza*(tr[id<<1|1].r-tr[id<<1|1].l+1); tr[id<<1|1].lazm*=tr[id].lazm; tr[id<<1|1].laza=tr[id<<1|1].laza*tr[id].lazm+tr[id].laza; tr[id].lazm=1;tr[id].laza=0; tr[id<<1].check();tr[id<<1|1].check(); } #define mu tr[id].val=tr[id<<1].val+tr[id<<1|1].val void buildtree(ll id,ll l,ll r){//建树 tr[id].l=l,tr[id].r=r,tr[id].laza=0,tr[id].lazm=1; if(l==r){tr[id].val=a[l];tr[id].check();return;} ll mid=l+r>>1; buildtree(id<<1,l,mid); buildtree(id<<1|1,mid+1,r); mu; tr[id].check(); } void treeadd(ll al,ll ar,ll val,ll id){//区间加 ll l=tr[id].l,r=tr[id].r,mid=l+r>>1; if(al>r||ar<l)return; if(al<=l&&ar>=r){ tr[id].val+=(r-l+1)*val; tr[id].laza+=val; tr[id].check(); return ; } pushdown(id); treeadd(al,ar,val,id<<1); treeadd(al,ar,val,id<<1|1); mu; tr[id].check(); } void trmultiply(ll al,ll ar,ll val,ll id){//区间乘 ll l=tr[id].l,r=tr[id].r,mid=l+r>>1; if(al>r||ar<l)return; if(al<=l&&ar>=r){ tr[id].val*=val; tr[id].laza*=val; tr[id].lazm*=val; tr[id].check();return; } pushdown(id); trmultiply(al,ar,val,id<<1); trmultiply(al,ar,val,id<<1|1); mu; tr[id].check(); } ll query(ll id,ll al,ll ar){//区间和 ll l=tr[id].l,r=tr[id].r,mid=l+r>>1; if(al>r||ar<l)return 0; if(al<=l&&ar>=r)return tr[id].val; pushdown(id); ll ans=query(id<<1,al,ar); ans+=query(id<<1|1,al,ar); ans%=p; return ans; } int main(){ scanf("%lld%lld%lld",&n,&m,&p);//挺容易理解的,别忘了用%lld输出,第二次提交爆零的原因 for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);buildtree(1,1,n); for(ll i=0;i<m;i++){ ll x,y,k,b; scanf("%lld%lld%lld",&b,&x,&y); if(b==3){ printf("%lld\n",query(1,x,y)); } else{ scanf("%lld",&k); if(b-1) treeadd(x,y,k,1); else trmultiply(x,y,k,1); } } return 0; }

    P.S.一开始冗余操作挺多的,某些玄学的客观因素加成下超时了一个点T_T。。再次提交同一份代码卡时通过,删去一部分可以确定无用的取模后,用时440ms。。。应该还有一些取模是不必要的,不过懒得删了,手动滑稽

    最新回复(0)