At the main street of Byteland, there will be built n skyscrapers, standing sequentially one next to other. If look leftside right, sequence of their height will be a1, a2, . . . , an.
Initially the street is empty, every skyscraper’s height is 0. Hamster is the leader of the construction team. In each stage, Hamster can select a range [l, r], then the team will work on this range. Specifically, assume the height sequence is h1, h2, . . . , hn, then hl , hl+1, . . . , hr will increase by 1 during this stage. When hi = ai holds for all i ∈ [1, n], the project will be closed.
The plan may be changed for many times. There will be m events of 2 kinds below:
• 1 l r k (1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 105 ), for all x ∈ [l, r], change ax to ax + k.
• 2 l r (1 ≤ l ≤ r ≤ n), assume a1, a2, . . . , al−1, ar+1, ar+2, . . . , an = 0, ask for the minimum number of required stages to close the project.
Input
The first line of the input contains an integer T(1 ≤ T ≤ 1000), denoting the number of test cases. In each test case, there are two integers n, m(1 ≤ n, m ≤ 100000) in the first line, denoting the number of skyscrapers and events. In the second line, there are n integers a1, a2, ..., an(1 ≤ ai ≤ 100000). For the next m lines, each line describes an event. It is guaranteed that ∑n ≤ 10^6 and ∑m ≤ 10^6 .
Output
For each query event, print a single line containing an integer, denoting the answer
Input
1
5 4
1 3 1 4 5
2 1 5
1 3 4 2
2 2 4
2 1 5
Output
7
6
6
题意:相当于维护两种操作,操作一是让[L,R]区间加上k,操作二是你可以每次使得某区间-1,问多少次能使得[L,R]变成0(题意是从0开始每次区间+1变成该区间,两种方向都一样其实)。
解析:操作一我们很容易想到差分,因此对于第一种操作我们维护一个差分数组即可,可以用树状数组来维护,操作二其实跟道路铺设问题相同,做法不唯一,这里用差分思想(比较冷门),转为差分数组之后,问题就变成了每次选择一个数+1或者-1,或者选择两个数分别-1,+1,问最少多少次使得全部数变成0,贪心优先选两个数的方式,选一个正数-1,选一个负数+1,然后最后只剩下正数或者负数,次数再加上他的大小即可,其实最后可以发现如果正数和为x,负数和为y,那么次数就是max(x,y),因此我们可以用线段树维护区间的正数和与负数和即可。
注意:还需要维护原序列的原因是因为询问操作是相当于单独只看[L,R]这个区间,因此该区间的差分数组第一个数是原序列的a[l]值,答案就是[L+1,R]的正数和+a[l]与负数和的max即可
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll a[N],b[N];
struct s
{
ll l,r,x,y;//x,y是正数和与负数和
}tr[N*4];
void pushup(int u)
{
tr[u].x=tr[u*2].x+tr[u*2+1].x;
tr[u].y=tr[u*2].y+tr[u*2+1].y;
}
void build(int u,int l,int r)
{
if(l==r)
{
if(b[l]>0) tr[u]={l,r,b[l],0};//正数
else tr[u]={l,r,0,b[l]};//负数
}
else
{
tr[u]={l,r};
int mid=l+r>>1;
build(u*2,l,mid);
build(u*2+1,mid+1,r);
pushup(u);
}
}
void modify(int u,int x,ll v)
{
if(tr[u].l==tr[u].r&&tr[u].l==x)
{
if(tr[u].x>0)//如果该节点是存贮正数
{
ll p=tr[u].x+v;
tr[u].x=0;
if(p>0) tr[u].x=p;
else tr[u].y=p;
}else//如果该节点是存贮负数
{
ll p=tr[u].y+v;
tr[u].y=0;
if(p>0) tr[u].x=p;
else tr[u].y=p;
}
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) modify(u*2,x,v);
else modify(u*2+1,x,v);
pushup(u);
}
ll query1(int u,int l,int r)//询问区间正数和
{
if(l<=tr[u].l&&r>=tr[u].r) return tr[u].x;
int mid=tr[u].l+tr[u].r>>1;
ll ans=0;
if(r<=mid) ans+=query1(u*2,l,r);
else if(l>mid) ans+=query1(u*2+1,l,r);
else ans+=query1(u*2,l,r)+query1(u*2+1,l,r);
return ans;
}
ll query2(int u,int l,int r)//询问区间负数和
{
if(l<=tr[u].l&&r>=tr[u].r) return tr[u].y;
int mid=tr[u].l+tr[u].r>>1;
ll ans=0;
if(r<=mid) ans+=query2(u*2,l,r);
else if(l>mid) ans+=query2(u*2+1,l,r);
else ans+=query2(u*2,l,r)+query2(u*2+1,l,r);
return ans;
}
ll trr[N];//树状数组
int n,m;
int lowbit(int x)
{
return x&-x;
}
void add(int x,ll c)
{
for(int i=x;i<=n;i+=lowbit(i)) trr[i]+=c;
}
ll sum(int x)//询问a[x]的值
{
int ans=0;
for(int i=x;i;i-=lowbit(i)) ans+=trr[i];
return ans;
}
void solve()
{
memset(trr,0,sizeof trr);//多组清空
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),b[i]=a[i]-a[i-1];
for(int i=1;i<=n;i++) add(i,b[i]);
build(1,1,n);
while(m--)
{
int op,l,r,k;
scanf("%d%d%d",&op,&l,&r);
if(op==1)
{
scanf("%d",&k);
add(l,k),add(r+1,-k);//原序列差分维护
modify(1,l,k);
if(r