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

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

    Description

    给你n个数;
    它们是由(1..n)组成的排列;
    然后给你一个数字k;
    让你求这个序列的所有长度大于等于k的区间的第k大值的和;

    Solution

    数组模拟链表;
    我们从小到大枚举数字x,寻找以x为第k大的数字的区间有多少个;
    数组中只保留了了大于等于x的数字的信息
    (即每次做完数字x为第k大的区间之后,把x删掉);
    每次在做x的时候;
    在x的左半部分找最近的比它大的k-1个数字,
    然后从那第k-1个数字开始,将其位置设置为L,然后初始时刻,右端点位置R设置为x的位置;
    因为模拟链表的数组中,存放的都是大于等于x的数字
    所以当前的(L..R)这一段的第k大值肯定是数字x的;
    (这时设L再往左跳一次会到达tL,R向右跳一次会到达tR);
    (则答案加上x*(L-tL)*(tR-R),因为tL+1..L中任意一个数字作为区间左端点,R..rR-1中任意一个数字作为区间右端点,这些区间的k大值都是x)
    然后左端点L肯定不能再往左移动了,不然x就不是第k大数了;
    于是L往右移动一次;
    同时R也跟着往右移动一次
    (中间小于x的数字已经在之前的操作中删掉了,可以直接跳到下一个大于等于x的数字)
    这样就能保证(L+1,R+1)这段区间也满足x是k大值;
    再用上面的增加答案的方法增加答案就好;
    直到L变成x为止
    可能x左边没有k-1个大于它的数字;
    那么就不用找那么多;
    然后R提前先往右跳缺少(即却几次才够k-1个数字)次就好;

    NumberOf WA

    1

    Reviw

    利用链表这一工具,快速获取左边和右边最近的若干个大于某个数的位置;
    再思考一下如何固定x为第k大值;
    再利用乘法原理求出区间个数;
    每个区间对答案贡献一样.
    从而求出答案;

    Code

    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    
    const int N = 5e5;
    
    int n,T,k,a[N+100],b[N+100][2],pos[N+100];
    
    void del(int x){
        int pre = b[x][0],after = b[x][1];
        b[pre][1] = after;
        b[after][0] = pre;
    }
    
    int main(){
        scanf("%d",&T);
        while (T--){
            LL ans = 0;
            scanf("%d%d",&n,&k);
            for (int i = 1;i <= n;i++){
                scanf("%d",&a[i]),b[i][0] = i-1,b[i][1] = i+1;
                pos[a[i]] = i;
            }
    
            for (int x = 1;x <= n;x++){
                int i = pos[x],cntr = 0;
                for (int j = 1;j <= k-1;j++){
                    int ti = b[i][1];
                    if (ti > n) break;
                    cntr++;
                    i = ti;
                }
    
                int l = pos[x];
                for (int j = 1;j <= (k-1-cntr);j++)
                    l = b[l][0];
    
                for (int j = i;j >= pos[x] && l;j = b[j][0],l = b[l][0])
                    ans += 1LL*x*(l-( (b[l][0]) > 0 ? b[l][0]:0))*
                                ( (b[j][1] > n? (n+1):b[j][1]) - j);
                del(pos[x]);
            }
    
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    gulp安装
    ssh公钥自动登陆
    Laravel 依赖注入原理
    mac添加环境变量
    get和post的区别
    CPU进程与线程的关系和区别
    微信支付开发+{ping++}微信支付托管
    git学习笔记
    消除 activity 启动时白屏、黑屏问题
    转:android中APK开机自动运行
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7626143.html
Copyright © 2011-2022 走看看