CSU-2151 树状数组(同时涉及区间覆盖和区间修改,思路)

题目链接(心塞塞)

         ——八月月赛最惨不忍睹的一道题……对不起是我太菜了,本来吸取上次的教训想要先把题看一遍找出签到题,看到D题(对没错就是这道题)感觉妈耶,这不是线段树的板题吗,签到题是它没跑了T_T,结果卡了整场……最后发现真正的巨水无比签到题是E。果然我这种菜鸡一定一定一定要把题目看完啊。。。

       如果它是一道普普通通的板题,也许这次月赛就不会有那么多可爱的同学们在这棵树上卡死了,所以关键是,它似乎并没有看起来那么水……真的欺骗我幼小的心灵嘤嘤嘤

       好吧收起我悲愤的心情言归正传。

---------------------------------------------------------------------OoO--------------------------------------------------------------------------------

题目:

小L正在组织acm暑假集训,但众所周知,暑假集训的萌新中有OI神犇,也有暑假才开始学算法的萌新,如果统一集训的难度,无法很好地让萌新们得到训练,所以小L想了一个办法,根据每次测试的情况,改变萌新们的集训难度。现在将萌新们编号为1到n,最初萌新们的集训难度为v0,测试后有两种操作,第一种是某一区间的萌新的集训难度同时提高,另一种是将某一段区间的萌新的集训难度变为同一个数,同时,Wells希望在某次调整难度之后,知道某一段区间的萌新的集训难度之和,由于小L比较鶸,他并不知道如何快速解决这个问题,你能帮帮他嘛?

 

Input

第一行三个数n,m,v0 表示有n名萌新和m次调整,初始时全部萌新的集训难度都为v0

第2~m+1行 每行三个数或四个数

0 x y v 表示把 [x,y]区间内的萌新的集训难度都增加v

1 x y v 表示把 [x,y]区间内的萌新的集训难度都变为v

2 x y表示询问[x,y]区间内萌新的集训难度之和

0

Output

每个询问一行,输出答案

Sample Input

3 5 0
0 1 3 1
1 2 3 2
2 1 1  
2 2 2
2 2 3

Sample Output

1
2
4

Hint

Source

某场noip模拟题

Author

lfw

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

      这个题唯一不同的地方就是它的操作同时有区间覆盖和区间修改,乍一看两个都是板子,但放在一个程序里就很麻烦,各种凌乱……

      我们知道,区间覆盖和区间修改这两个问题分开的时候,我们都用了一个tag标记的,tag承载了区间们需要覆盖或者修改的值。而对于这道题,我们可以很明显感觉到一个tag并不能保存下足够的信息,无法区分是要修改还是覆盖,这时就很自然的想到:不如开两个?一个保存覆盖值、一个保存修改值,hiahiahia(下面我们令tag1储存区间修改的值,tag2储存区间覆盖的值)

      那么,这时我们要考虑的就是如何把tag们保存下来的信息传递给它的儿子,换句话说,就是怎么根据tag来对子树区间的值进行修改?

      当我们要进行区间修改的时候,指的是这个区间的数都同时加上某个数,有两种情况:

          1、这个数的子树区间的tag2为空,那么子树的tag1+=父亲的tag1;

          2、子树的tag2不为空,就是说子树正在进行区间覆盖,而我们知道区间覆盖是把整个区间的数直接变成另外一个数,而我们又要在这基础上加上一个数,可以想到我们可以直接把这个tag2变成加完之后的数,即此时,子树的tag2+=父亲的tag1;

      当我们进行区间赋值的时候,

         很显然子树如果正在进行区间修改,要中断这个过程,因为无论它加成了什么样,最后都要变成这个数,所以此时

        子树的tag1=初始值,子树的tag2=父亲的tag2;

把tag们的情况想清楚后,其他的就跟普通的板子没什么区别了(没写出来的菜鸡毫无防备地流下了泪水……

emmmmm代码有点长……

--------------------------------------------------------------我是分割线哇-------------------------------------------------------------------------

代码:

#include
#include
#define maxn 100005
#define INF -2000000001

using namespace std;
typedef long long ll;
struct node
{
     ll l,r,sum;
     ll tag1,tag2;//tag1表示加数,tag2表示改数
}tree[maxn<<2];

void build(ll k,ll l,ll r,ll x)//建造初始树
{
     tree[k].l=l;tree[k].r=r;
     tree[k].tag1=INF;tree[k].tag2=INF; //记得把tag1和tag2初始化成运算过程中不会出现的数
     if(l==r)
     {
          tree[k].sum=x;
          return;
     }
     ll mid=(l+r)>>1;
     build(k<<1,l,mid,x);build(k<<1|1,mid+1,r,x);
     tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}

void change(ll k)  //对下面子树进行信息改变的函数
{
     if(tree[k].l!=tree[k].r)
     {
          if(tree[k].tag2!=INF)//根据我们上面的分析,除了最开始,tag1和tag2肯定有且只有一个包含信息
          {
               ll v=tree[k].tag2;
               tree[k<<1].sum=(tree[k<<1].r-tree[k<<1].l+1)*v;
               tree[k<<1|1].sum=(tree[k<<1|1].r-tree[k<<1|1].l+1)*v;
               tree[k<<1].tag2=tree[k<<1|1].tag2=v;
               tree[k<<1].tag1=tree[k<<1|1].tag1=INF;
               tree[k].tag2=INF;
          }
          else
          {
               ll v=tree[k].tag1;
               tree[k<<1].sum+=(tree[k<<1].r-tree[k<<1].l+1)*v;
               tree[k<<1|1].sum+=(tree[k<<1|1].r-tree[k<<1|1].l+1)*v;
               if(tree[k<<1].tag2!=INF)
                    tree[k<<1].tag2+=v;
               else
                    tree[k<<1].tag1=(tree[k<<1].tag1==INF)?v:tree[k<<1].tag1+v;
               if(tree[k<<1|1].tag2!=INF)
                    tree[k<<1|1].tag2+=v;
               else
                    tree[k<<1|1].tag1=(tree[k<<1|1].tag1==INF)?v:tree[k<<1|1].tag1+v;
               tree[k].tag1=INF;
          }
     }
}
void add(ll k,ll l,ll r,ll x,ll c)
{
     if(tree[k].tag1!=INF||tree[k].tag2!=INF)//如果tag1和tag2其中有一个承载着信息
     {
          change(k);
     }
     if(c==0)
          tree[k].sum+=(r-l+1)*x;
     if(tree[k].l==l&&tree[k].r==r)
     {
          if(c==0)  //加上某个数
		{
			if(tree[k].tag2!=INF)
				tree[k].tag2+=x;
			else
				tree[k].tag1=(tree[k].tag1==INF)?x:tree[k].tag1+x;//如果tag1存在,就累加x
		}
		else  //改成某个数
		{
			tree[k].sum=(r-l+1)*x;
			tree[k].tag1=INF;tree[k].tag2=x;//对tag2进行赋值
		}
		return;
     }
     ll mid=(tree[k].l+tree[k].r)>>1;
     if(l>=mid+1)
          add(k<<1|1,l,r,x,c);
     else if(r<=mid)
          add(k<<1,l,r,x,c);
     else
          add(k<<1,l,mid,x,c),add(k<<1|1,mid+1,r,x,c);
     tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
ll query(ll k,ll l,ll r)
{
     ll res;
     if(tree[k].tag2!=INF||tree[k].tag1!=INF)
          change(k);
     //
     if(tree[k].l==l&&r==tree[k].r)
          return tree[k].sum;
     ll mid=(tree[k].l+tree[k].r)>>1;
     if(l>=mid+1)
          res=query(k<<1|1,l,r);
     else if(r<=mid)
          res=query(k<<1,l,r);
     else
          res=query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
     tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
     return res;
}
int main(void)
{
     ll n,m,c,x,y;
     ll v0,v;
     scanf("%lld%lld%lld",&n,&m,&v0);
     build(1,1,n,v0);
     for(ll i=0;i

呼呼

(还是觉得很心酸,想吐槽自己……)

你可能感兴趣的:(线段树)