zoukankan      html  css  js  c++  java
  • 第三场 hdu 6058 Kanade's sum(思维题+set)

    http://acm.hdu.edu.cn/showproblem.php?pid=6058

    题目大意:把一个数组分成若干子部分,求每部分中第k大的数的和是多少?

    解题思路:首先能够想到的最暴力的算法是:枚举数组的每一个子部分二重for循环,然后求出每一段的第k大的数又是一重for循环。这样的想法时间复杂度O(n^3)

    看一下数据就知道上面的想法肯定是不行的。

    然后能够想到的还有就是对于数组中的数x,我们只要找到x的左边有k个比x大的数的位置,然后在右边找出k个比x大的数就行了。

    首先暴力去找左边和右边的数肯定是不行的。我们可以通过set容器自动排序这一特性,实现这一想法。我们知道x的左边就意味着它的下标是比x的下标要小,同理,x的有边就意味着它的下标是比x的下标要大,所以我们只要把x的下标放入set中就可以了。然后我们在找 x 的左边时要求每一次都能够找到下一个比当前x大的值。这样就需要用数组用来存储它的左边和右边的下一个比他大的位置。最后我们在左边统计出k个位置对应着右边的k个位置,计算他们的位置差的乘积就可以了。具体内容,请看代码

    AC代码:

     1 #include <iostream>
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 const int maxn=500005;
     5 int vis[maxn],left1[maxn],right1[maxn],nowl,nowr,nextl,nextr,l,r;
     6 int main()
     7 {
     8     int t,n,k;
     9     //freopen("1003.in","r",stdin);
    10     //freopen("1003.out","w",stdout);
    11     scanf("%d",&t);
    12     while(t--)
    13     {
    14         scanf("%d%d",&n,&k);
    15         int x;
    16         for(int i=1;i<=n;i++)
    17         {
    18             scanf("%d",&x);
    19             vis[x]=i;//处理数据也更方便索引
    20             left1[i]=0;
    21             right1[i]=n+1;
    22         }
    23         set<int>s;
    24         set<int>::iterator ite;
    25         left1[n+1]=0;
    26         right1[n+1]=n+1;
    27         s.insert(0);
    28         s.insert(n+1);//默认第一个位置和最后一个位置上是最大值
    29         long long ans=0;
    30         for(int i=n;i>0;i--)
    31         {
    32             s.insert(vis[i]);//由大到小插入x
    33             ite=s.find(vis[i]);//x插入的所在位置,它左边的数都大于x且有ite-1个,对应序列中x左边大于x的个数
    34             ite++;
    35             r=*ite;//右边最靠近x的vis值
    36             l=left1[r];//左边最靠近x的vis值
    37             left1[r]=vis[i];
    38             right1[vis[i]]=r;
    39             right1[l]=vis[i];
    40             left1[vis[i]]=l;//把x插入l和r之间,这样很方便索引
    41             if(n-i+1<k)
    42             continue;
    43             nowl=vis[i];
    44             for(int j=0;j<k&&nowl;j++)//左端找出k个比x大的数,有可能左端不足k个
    45             nowl=left1[nowl];
    46             nowr=nowl;
    47             for(int j=0;j<k&&nowr!=n+1;j++)//从左端位置往右端找,凑足k个数
    48             nowr=right1[nowr];
    49             for(int j=0;j<k;j++)
    50             {
    51                 if(nowl==vis[i]||nowr==n+1)//k个数里面一定要包含x,越过了x就可以跳出了
    52                 break;
    53                 nextl=right1[nowl];
    54                 nextr=right1[nowr];//这个位置到下一个位置之间是可以任意选择的,所以计算乘积就好了
    55                 ans+=1ll*(nextl-nowl)*(nextr-nowr)*i;
    56                 nowl=nextl;
    57                 nowr=nextr;
    58             }
    59         }
    60         printf("%lld
    ",ans);
    61     }
    62     return 0;
    63 }
  • 相关阅读:
    浅谈sqlserver数据库优化(一)----开光篇
    代码生成器的意愿
    面试的那些小事。
    Access和Sql区别
    牢记!SQL Server数据库开发的二十一条注意点
    SQL SERVER 与ACCESS、EXCEL的数据导入导出转换
    ResponseUtil jackson 转换问题;返回结果与 Bean 之间的转换,推荐使用convertValue
    阿里巴巴 ali1688 Date +0800的问题
    Spring Boot 全局异常。RestControllerAdvice,ControllerAdvice
    controller 层 date 类型的参数,SpringBoot自动转换 dateformat
  • 原文地址:https://www.cnblogs.com/wang-ya-wei/p/7273537.html
Copyright © 2011-2022 走看看