zoukankan      html  css  js  c++  java
  • 排列

    描述

    2.排列

    (sum.cpp/c/pas)

    时间限制:1s

    内存限制:256MB

    【问题述】

    给出一个随机的排列,请你计算最大值减最小值的差小于等于0~n-1的区间分别有多少个。

    输入格式

    输入文件名为sum.in。

    第一行一个数T(<=10),表示数据组数

    对于每一组数据:

    第一行一个数n(1<=n<=100,000)

    第二行n个数a1...an,表示一个随机的排列

    【输出】

    输出文件名为sum.out。

    对于每组数据输出n行,分别表示差值小于等于0~n-1的区间个数

    【输入输出样例】

    sum.in

    sum.out

    1

    4

    3 2 4 1

    4

    5

    7

    10

    【数据说明】

    对于30%的数据,1<=n<=300;

    对于60%的数据,1<=n<=5000

    对于100%的数据,1<=n<=100000

    思路:

        暴力做法很简单,找出区间最大,最小值,再加到那个值所对应的数组中,最后求前缀和就行了。

      但跑得太慢了。因为数据是随机的,所以最大值和最小值变化的频率并不高,

      也就是说,那个最大值和最小值的差是一段一段的(A,A,A,A,A,B,B,B,B,C,C这种样子)。

      这样的话,只需要找出每段区间的长度,然后把长度加到ans里就行了。

      在这里维护一个单调栈,站里记录单调序列元素的位置(也就是每段连续区间的端点)。

      这样就实现了每下都能走一段连续区间,降低了复杂度。(因为不用一个一个的走了)。

    #include<iostream>
    #include<queue>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=100100;
    long long  ans[N];
    int a[N],mnn[N],mxn[N];
    int t,n,cnt1,cnt2;
    void PUSH(int x)
    {
        mxn[++cnt1]=x;
        while(cnt1>1&&a[mxn[cnt1-1]]<a[mxn[cnt1]])
            cnt1--,mxn[cnt1]=mxn[cnt1+1];
        
        mnn[++cnt2]=x;
        while(cnt2>1&&a[mnn[cnt2-1]]>a[mnn[cnt2]])
            cnt2--,mnn[cnt2]=mnn[cnt2+1];    
    }
    int main()
    {
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            memset(ans,0,sizeof(ans));
            for(int i=1;i<=n;i++)
                scanf("%d",&a[i]);
            ans[0]=n;
            cnt1=cnt2=0;
            mxn[++cnt1]=mnn[++cnt2]=n;
            for(int L=n-1;L>=1;L--)
            {
                PUSH(L);
                int last=n+1,x=1,y=1,now;
                while(mxn[x]!=mnn[y])
                {
                    now=max(mxn[x],mnn[y]);
                    ans[a[mxn[x]]-a[mnn[y]]]+= 1LL*(last-now);
                    last=now;
                    if(mxn[x]>mnn[y])    x++;
                    else y++;
                }
            }
            for(int i=1;i<n;i++)
                ans[i]+=ans[i-1];
            for(int i=0;i<n;i++)
                printf("%lld
    ",ans[i]);
        }
        return 0;
    }
  • 相关阅读:
    右击DataGrilView行事件
    winForm textBox的数字输入验证
    C#获取本月开始日期和结束日期
    异步加载js文件并执行js方法:实现异步处理网页的复杂效果
    Windows Phone 7 IEnumerable<T>.Select和SelectMany的区别
    Windows Phone 7 Perst嵌入式数据库的学习
    Windows Phone 7 Coding4Fun的弹出框
    MVVM模式介绍
    Windows Phone 7 网络编程之天气预报应用
    Windows Phone 7 扩展TextBox控件为数字输入文本框
  • 原文地址:https://www.cnblogs.com/CLGYPYJ/p/7620008.html
Copyright © 2011-2022 走看看