zoukankan      html  css  js  c++  java
  • 2017 Multi-University Training Contest

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6058

    题目意思:给你一个排列,求所有区间长度大于等于k的区间第k大的数的和……

    思路:一开始看到区间k大?结果是所有区间,没那么简单,队友拿一个划分树的模板直接TLE,最后也没有做出来。思路是算出每个点在多少个区间内是第k大的,转换一下问题,找一个区间有k-1个比这个数大的,剩下的数都比他小,这样区间的个数乘以这个数就是这个数的贡献,所以关键在于找那些比这个数大的数都在哪些位置上关键,最好还是从小到大的,前面的思路比赛的时候想到了,就是后面这个优化没想出来,看了题解发现是用链表,感觉真的很6,算是学到了。

    具体做法我们建立一个链表这个链表每个点有三个参数pos,pre,nxt,由于我用的是结构体数组建的链表,所以每个点的下标代表他是整个排列中第几小的元素,pos代表他在排列中的位置,pre代表每个点的前续元素的pos,nxt代表他后续元素的pos,刚开始的时候肯定要初始化一下。然后我们先从整个排列中值最小的点开始枚举,这样链表内所有的点都比他大,然后再删除他,这样每次枚举一个点的时候链表中所有其他的点都会比它大,然后每次先往后找k个,再往前找k个。然后卡一个k+1个点的区间。左右区间差乘在一起,最后每个点的贡献加在一起,然后画一个图确定一下边界问题,xjb写一下代码就好了。

    代码:

     1 //Author: xiaowuga
     2 #include <iostream>
     3 #include <algorithm>
     4 #include <set>
     5 #include <vector>
     6 #include <queue>
     7 #include <cmath>
     8 #include <cstring>
     9 #include <cstdio>
    10 #include <ctime>
    11 #include <map>
    12 #include <bitset>
    13 #include <cctype>
    14 #define maxx INT_MAX
    15 #define minn INT_MIN
    16 #define inf 0x3f3f3f3f
    17 #define mem(s,ch) memset(s,ch,sizeof(s))
    18 #define nc cout<<"nc"<<endl
    19 const long long N=5*100000+10;
    20 using namespace std;
    21 typedef long long LL;
    22 const int MAXBUF = 10000;
    23 char buf[MAXBUF], *ps = buf, *pe = buf+1;
    24 inline void rnext()
    25 {
    26     if(++ps == pe)
    27         pe = (ps = buf)+fread(buf,sizeof(char),sizeof(buf)/sizeof(char),stdin);
    28 }
    29 
    30 template <class T>
    31 inline bool readin(T &ans)
    32 {
    33     ans = 0;
    34     T f = 1;
    35     if(ps == pe) return false;//EOF
    36     do{
    37         rnext();
    38         if('-' == *ps) f = -1;
    39     }while(!isdigit(*ps) && ps != pe);
    40     if(ps == pe) return false;//EOF
    41     do
    42     {
    43         ans = (ans<<1)+(ans<<3)+*ps-48;
    44         rnext();
    45     }while(isdigit(*ps) && ps != pe);
    46     ans *= f;
    47     return true;
    48 }
    49 struct node{
    50     int pos,pre,nxt;
    51 }p[N];
    52 int n,k;
    53 LL ans=0;
    54 void read(){
    55    for(int i=1;i<=n;i++){
    56        int x; readin(x);
    57        p[x].pos=i;
    58        p[i].pre=i-1;
    59        p[i].nxt=i+1;
    60    } 
    61    //把两个边界插进去
    62    p[0].pre=0;
    63    p[n+1].nxt=n+1;
    64 }
    65 void solve(){
    66     int lc,rc,rq[85];
    67     LL tans;
    68     for(int i=1;i<=n;i++){
    69        lc=rc=tans=0;
    70        int t=p[i].pos;
    71        for(int j=t;j<=n&&rc<k;j=p[j].nxt){//该节点往前找k个比整个排列第i大的数大的数
    72             rq[++rc]=p[j].nxt-j;
    73        }
    74        for(int j=t;j>0&&lc<k;j=p[j].pre){//该节点往后找k个比整个排列第i大的数大的数
    75             lc++;
    76             if(k-lc+1>rc) continue;//左右找到的数量大于k的时候,开始计算区间
    77             tans+=(j-p[j].pre)*rq[k-lc+1];//此时找到的区间和第一个找到的右区间匹配 
    78        }
    79        ans+=tans*i;//计算该点贡献
    80        //删除节点
    81        p[p[t].pre].nxt=p[t].nxt;
    82        p[p[t].nxt].pre=p[t].pre;
    83     }
    84     cout<<ans<<endl;
    85 }
    86 int main() {
    87     int T;
    88     readin(T);
    89     while(T--){
    90         readin(n);readin(k);
    91         k=min(k,80);
    92         read();
    93         ans=0;
    94         solve(); 
    95     }
    96     return 0;
    97 }
    View Code

    总结:通过从小到大枚举1-n,枚举完一个节点在链表中把他删除,每次枚举的时候这个点都是链表中最小的元素,放心往前往后找k个都是比他大的,感觉这个姿势好厉害。

    认识到的不足:可能在确定边界上总是懵逼,以后决定使用画图举例的方式确定边界,然后这个链表的姿势确定是不会,未来可能还得多复习复习这个题。

  • 相关阅读:
    数据库小记:根据指定名称查询数据库表名及根据指定名称查询数据库所有表中的字段名称(支持mysql/postgre)
    Java实现 LeetCode第30场双周赛 (题号5177,5445,5446,5447)
    Java实现第十一届蓝桥杯C/C++ 大学 B 组大赛软件类 省赛真题(希望能和各位大佬能一起讨论算法题:讨论群:99979568)
    Java实现第十一届蓝桥杯 省赛真题(希望能和各位大佬能一起讨论算法题:讨论群:99979568)
    Java引用类型之软引用(2)
    Java引用类型之软引用(1)
    Java引用类型
    对象的创建
    类的初始化
    初始化itable
  • 原文地址:https://www.cnblogs.com/xiaowuga/p/7272742.html
Copyright © 2011-2022 走看看