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

· · 题解

**Orz写题解的同时顺便说说自己的理解,哪里不对的地方求大佬斧正orz**
~~作为一个快被淘汰的Pascal选手(逃)
一本正经的写下这个Pascal题解(逃)~~
下面是代码,题解会标在代码上...

------------

program ex1;
var i,j,k,n,m,p,x,y,data,cnt,size:longint;
    a:array[0..100000] of int64;
    tree,mul,lazy:array[0..400007] of int64;   f1:text;
//记得开int64,也就是C++中的long long 不然会算术上溢,这也是RE的错误之一了~~(亲身经历233)~~
Procedure pushdown(p,l,r:longint);//标记下传,注意先乘后加
var mid:longint;
begin
  if (mul[p]=1)and(lazy[p]=0)then exit;
  if (l=r) then
  begin
    mul[p]:=1;
    lazy[p]:=0;
    exit;
  end;

  mid:=(l+r) shr 1;
  tree[p shl 1]:=(tree[p shl 1]*mul[p]+lazy[p]*(mid-l+1)) mod size;//mid-l+1与r-l+1同理
  tree[p shl 1 or 1]:=(tree[p shl 1 or 1]*mul[p]+lazy[p]*(r-mid)) mod size;//r-mid就是区间长度,不用加1的原因很简单,因为此时的mid的位置是不算滴!

  mul[p shl 1]:=(mul[p]*mul[p shl 1]) mod size;
  mul[p shl 1 or 1]:=(mul[p]*mul[p shl 1 or 1]) mod size;//下传乘法标记

  lazy[p shl 1]:=(lazy[p shl 1]*mul[p]+lazy[p])mod size;
  lazy[p shl 1 or 1]:=(lazy[p shl 1 or 1]*mul[p]+lazy[p]) mod size;//下传加法标记,同样还是先乘再加

  mul[p]:=1;
  lazy[p]:=0;//将标记初始化。
end;

Procedure change(p,l,r,x,y:longint);//乘法运算
var mid:longint;
begin
  if (r<x) or (l>y) then exit;
  if (l>=x) and (r<=y) then
  begin
    tree[p]:=(tree[p]*data) mod size;
    mul[p]:=(mul[p]*data) mod size;
    lazy[p]:=(lazy[p]*data) mod size;//mul数组代表乘的标记,lazy就是加的标记,乘法运算里二者都要更新
    exit;
  end;

  pushdown(p,l,r);
  mid:=(l+r) shr 1;
  if x<=mid then change(p shl 1,l,mid,x,y);//走左儿子
  if y>mid then change(p shl 1 or 1,mid+1,r,x,y);//走右儿子

  tree[p]:=(tree[p shl 1]+tree[p shl 1 or 1]) mod size;
end;

Procedure add(p,l,r,x,y:longint);//加法运算
var mid:longint;
begin
  if (r<x) or (l>y) then exit;
  if (l>=x) and (r<=y) then
  begin
    lazy[p]:=(lazy[p]+data) mod size;
    tree[p]:=(tree[p]+(r-l+1)*data) mod size;//此时区间为[l,r]那么区间长度就是r-l+1,所以当前区间和为原来的和tree[P]加上区间长度r-l+1乘每一位要加的data就是更新的tree[p]。
    exit;
  end;

  pushdown(p,l,r);
  mid:=(l+r) shr 1;
  if x<=mid then add(p shl 1,l,mid,x,y);
  if y>mid then add(p shl 1 or 1,mid+1,r,x,y);
  tree[p]:=(tree[p shl 1]+tree[p shl 1 or 1]) mod size;//要心心念念mod size
end;

Function count(p,l,r,x,y:longint):longint;
var mid:longint;
begin
  count:=0;
  if (r<x) or (l>y) then exit;//边界条件
  if (l>=x) and (r<=y) then
    exit(tree[p]);

  pushdown(p,l,r);
  mid:=(l+r) shr 1;//这句话相当于(l+r) div 2,某犇犇说位运算可以加速所以我就写了
  exit(count(p shl 1,l,mid,x,y)+count(p shl 1 or 1,mid+1,r,x,y) mod size);
end;

Procedure build(p,l,r:longint);
var mid:longint;
begin
  mul[p]:=1;
  lazy[p]:=0;//mul数组的初始化特别重要,即乘法标记初始要置为1才能进行后续操作
  if l=r then
  begin
    tree[p]:=a[l];

  end  else
  begin
    mid:=(l+r) shr 1;
    build(p shl 1,l,mid);
    build(p shl 1 or 1,mid+1,r);
    tree[p]:=tree[p shl 1]+tree[p shl 1 or 1];
  end;
  tree[p]:=tree[p] mod size;//时刻mod size Orz
end;

begin//主程序没什么好讲的Orz
  readln(n,m,size);

  for i:=1 to n do read(a[i]);
   build(1,1,n);
//我n天没过(并且找了很多大犇看都没发现问题)的地方就在这里...作为一个初学者,我居然...先build(建树)再read(读入)了啊啊啊啊啊啊啊啊
  for i:=1 to m do
  begin
    read(cnt);
    case cnt of
    1:begin
        readln(x,y,data);
        change(1,1,n,x,y);
      end;
    2:begin
        readln(x,y,data);
        add(1,1,n,x,y);
      end;
    3:begin
        readln(x,y);
        writeln(count(1,1,n,x,y) mod size);
      end;
    end;
  end;
end.

祝大家AC快乐~