The 13th Chinese Northeast Contest--H. Skyscraper(树状数组+线段树)

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

你可能感兴趣的:(c++,c语言,算法)