扶桑号战列舰【单调栈+线段树】

扶桑号战列舰

传送门  来源upc:12800

题目描述

众所周知,一战过后,在世界列强建造超无畏级战列舰的竞争之中,旧日本海军根据“个舰优越主义”,建造了扶桑级战列舰,完工时为当时世界上武装最为强大的舰只。
同时,扶桑号战列舰也是舰岛最为科幻的战列舰。
当然,要建造这样的舰船,科技水平是必须的。
同样众所周知的是,德意志科学技术天下第一,所以IJN的司令官从德国学来了一种先进的建船方法。
一只战舰横过来可以看做一个长度为n的序列,每个位置有一个数ai表示这个位置设计的高度。这种先进的造船技术可以每次将一个区间[l,r]内的所有位置高度都+1,求到达最终设计状态的最少操作次数。
如果你不能及时完成的话,IJN司令官会奖励你去参加苏里高海战。

输入

第一行包含一个整数n,表示序列的长度。
第二行包含n个非负整数a1,a2,a3,…,an,表示最终的状态。

输出

输出的第一行是一个正整数m,表示最少的操作次数。
接下来m行每行两个正整数li,ri,表示一次操作。
你需要保证1≤li≤ri≤n。
保证最少次数m≤105,输出可以以任意顺序输出。

样例输入

6
2 3 3 3 3 3

样例输出

3
1 6
1 6
2 6

提示

思路:(直接说不太好理解,看例子)

   输入:

          8

          4 5 6 5 4 2 10 4

1、输入的值当作高,用树状图将每个点表示出来:

2、用单调栈维护出左区间即左边第一个比它小的位置,右区间同理:(单调栈维护:传送门) 

x=1:   [1,5]    h=4

x=2:   [2,4]    h=5

...        ...       ...

x=8:   [7,8]    h=4

3、将上面的区间按照对应点的高从小到大去重并排序,。

h=2     [1,8]

h=4     [1,5]

h=4     [7,8]

 ...         ...

h=10   [7,7]

之所以还有两个4没被去重,是因为它们左右区间不同。

4、还原柱状图:

step1:    根据求得的区间,一层一层的将高度增加,增加后用flag数组记录这个点现在的高度。

(h=2     [1,8] )

step2:  由step1可知flag[1]=2,但第二个h=4,所以这个点的高度要再加2,加完后flag[1]要更新为4,这一步我试了用fill()来维护更新,但结果T了,所以要用树状数组(感谢:Wintob 的板子)。

(h=4     [1,5] )

step3:  同上

(h=4     [7,8] )

step4:同上

(h=5    [2,4] )

step5:同上

(h=6    [3,3] )

step6:同上

(h=10    [7,7] )

就得到了原来的柱形图。

具体输出看代码怎么实现的,不懂欢迎留言。

AC代码:(代码长是因为没想到其他好的办法维护flag数组,用树状数组更快但会长一些)

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5;
int n;
int a[MAX+5];
int top;
int Stack[MAX+5];
struct node1{
    int l;
    int r;
    int cnt; ///区间输出几次
}num[MAX+5];
struct node{
    int dir;
    int l;
    int r;
}edge[MAX+5],edge1[MAX+5];
bool cmp(node x,node y)
{
    return x.dir<y.dir;
}

struct node2
{
    int l,r;
    long long sum,poi;
}tree[4040400];
int m;
int c,l,r,x;
long long d;
long long a1[1010100];
void build(int p,int l,int r) // 建树 ///树状数组直接粘的板子,只是用来维护tree数组的
{
    tree[p].l=l;tree[p].r=r;
    if (l==r) {
		tree[p].sum=a1[l];
		return;
	}
    int mid=(l+r)/2;
    if (l<=mid) build(p*2,l,mid);
    if (r>mid) build(p*2+1,mid+1,r);
    tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
}
void spread(int p)
{
    if (tree[p].poi)
    {
        tree[p*2].sum+=(tree[p*2].r-tree[p*2].l+1)*tree[p].poi;
        tree[p*2+1].sum+=(tree[p*2+1].r-tree[p*2+1].l+1)*tree[p].poi;
        tree[p*2].poi+=tree[p].poi;
        tree[p*2+1].poi+=tree[p].poi;
        tree[p].poi=0;
    }
}
void change(int p,int l,int r,long long d)//  区间修改
{
    if (l<=tree[p].l&&tree[p].r<=r)
    {
        tree[p].sum+=(tree[p].r-tree[p].l+1)*d;
        tree[p].poi+=d;
        return;
    }
    spread(p);
    int mid=(tree[p].l+tree[p].r)/2;
    if (l<=mid) change(p*2,l,r,d);
    if (r>mid) change(p*2+1,l,r,d);
    tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
}
long long ask(int p,int l,int r)  //区间查询
{
    if (l>tree[p].r||r<tree[p].l) return 0;
    if (l<=tree[p].l&&tree[p].r<=r)
    return tree[p].sum;
    spread(p);
    int mid=(tree[p].l+tree[p].r)/2;
    long long val=0;
    if (l<=mid) val+=ask(p*2,l,r);
    if (r>mid) val+=ask(p*2+1,l,r);
    return val;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        edge[i].dir=a[i];
    }
    a[0]=-1,a[n+1]=-1;

    Stack[top]=0;
    for(int i=1;i<=n;i++){
        while(a[Stack[top]]>=a[i])  top--;
        edge[i].l=Stack[top]+1;
        Stack[++top]=i;
    }

    top=0;
    Stack[top]=n+1;
    for(int i=n;i>=1;i--){
        while(a[Stack[top]]>=a[i])  top--;
        edge[i].r=Stack[top]-1;
        Stack[++top]=i;
    }

    sort(edge+1,edge+n+1,cmp);

    int cnt=0;
    for(int i=1;i<=n;i++){
        if(!((edge[i].l==edge[i-1].l)&&(edge[i].l==edge[i-1].l)&&(edge[i].dir==edge[i-1].dir))){
            edge1[cnt].dir=edge[i].dir;
            edge1[cnt].l=edge[i].l;
            edge1[cnt++].r=edge[i].r;
        }
    }
    int sum=0,cnt2=0; ///sum是最后的总次数
    build(1,1,n); 
    for(int i=0;i<cnt;i++){
        int num1=edge1[i].dir-ask(1,edge1[i].l,edge1[i].l); ///计算还需要建多高
        num[cnt2].l=edge1[i].l,num[cnt2].r=edge1[i].r,num[cnt2++].cnt=num1;
        sum+=num1; 
        change(1,edge1[i].l,edge1[i].r,num1); ///更新tree数组
    }
    printf("%d
",sum);
    for(int i=0;i<cnt2;i++){
        for(int j=0;j<num[i].cnt;j++){
            printf("%d %d
",num[i].l,num[i].r);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ldu-xingjiahui/p/12407421.html