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


    扶桑号战列舰

    传送门  来源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;
    }
  • 相关阅读:
    需求调研-用户访谈
    kettle监控销售人员当月每天任务完成率_20161107周一
    MySQL学习_计算用户支付方式占比_20161104
    MySQL 数据底部出现总计字样 第二种办法 纵向合并 20161103
    MySQL 单笔订单满6个及以上产品且金额>=300赠送优惠券_20161103
    MySQL学习_查看各仓库产品的销售情况_20161102
    MySQL与EXCEL sum sumif sumifs 函数结合_品牌汇总_20161101
    MySQL_活动期间单笔订单最高的且满600元 判别是重激活客户还是10月注册客户_20161031
    MySQL活动期间订单满600元并且在活动日期之前超过30天没有下过单_20161030
    MySQL活动期间制定月份注册用户下单情况_20161029
  • 原文地址:https://www.cnblogs.com/ldu-xingjiahui/p/12407421.html
Copyright © 2020-2023  润新知