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个都是比他大的,感觉这个姿势好厉害。

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

  • 相关阅读:
    WebView Android 调用js且须要获取返回结果
    推荐系统--揭开推荐的神奇面纱
    回调函数
    对CAB文件进行数字签名
    adodb.RecordSet的属性和方法
    Code Review中的几个提示
    Linux下find命令具体解释
    html5中关于input使用方法的改变
    关于 ioctl 的 FIONREAD 參数
    Grant的时候报错的解决:Access denied for user &#39;root&#39;@&#39;localhost&#39; (using password: YES)
  • 原文地址:https://www.cnblogs.com/xiaowuga/p/7272742.html
Copyright © 2011-2022 走看看