——八月月赛最惨不忍睹的一道题……对不起是我太菜了,本来吸取上次的教训想要先把题看一遍找出签到题,看到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<n,m<=10^5, |v|<=10^5
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<iostream>
#include<cstdio>
#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<m;++i)
{
scanf("%lld",&c);
if(c==1||c==0)
{
scanf("%lld%lld%lld",&x,&y,&v);
add(1,x,y,v,c);
}
else if(c==2)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(1,x,y));
}
}
return 0;
}
呼呼
(还是觉得很心酸,想吐槽自己……)