zoukankan      html  css  js  c++  java
  • HDU 6058 Kanade's sum (链表)

    2017ACM暑期多校联合训练 - Team 3 1003 Kanade’s sum

    Kanade’s sum

    Problem Description
    Give you an array A[1..n]of length n.

    Let f(l,r,k) be the k-th largest element of A[l..r].

    Specially , f(l,r,k)=0 if r−l+1<k.

    Give you k , you need to calculate ∑nl=1∑nr=lf(l,r,k)

    There are T test cases.

    1≤T≤10

    k≤min(n,80)

    A[1..n] is a permutation of [1..n]

    ∑n≤5∗105

    Input
    There is only one integer T on first line.

    For each test case,there are only two integers n,k on first line,and the second line consists of n integers which means the array A[1..n]

    Output
    For each test case,output an integer, which means the answer.

    Sample Input
    1

    5 2

    1 2 3 4 5

    Sample Output
    30

    题意:

    给你一个n个数的排列,问你全部区间第k大的总和为多少。

    分析:

    我们只要求出对于一个数x左边最近的k个比他大的和右边最近k个比他大的,扫一下就可以知道有几个区间的k大值是x。

    我们考虑从小到大枚举x,每次维护一个链表,链表里只有>=x的数,那么往左往右找只要暴力跳k次,删除也是O(1)的。

    时间复杂度:O(nk)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=5e5+7;
    int t,n,k,a[N],idx[N];///a表示的是这个数组,idx表示的是某个数在的位置
    struct Node
    {
        int pre,nxt,idx;
    } node[N];
    
    int main()
    {
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&k);
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]),idx[a[i]]=i;
                node[i]=Node {i-1,i+1,i};
            }
            node[n+1].idx=n+1;
            ll ans=0;
           for(int i=1;i<=n;i++)//找到当前这个数
            {
                int l=idx[i],r=idx[i];//左端点,右端点
                int cntl=1,cntr=0;//往前、往后找的数的个数
                while(cntl<k)//往前找,看有这个数的前面有多少个数
                {
                    if(node[l].pre==0)break;//左端点已经是第一个元素,就没结束
                    cntl++,l=node[l].pre;
                }
                while(cntl)//在前面有这么多数的基础上
                {
                    while(cntr+cntl>k)//左右区间的个数大于k了
                    {
                        cntr--,r=node[r].pre;//右区间往前移动
                    }
                    while(cntl+cntr<k)//左右区间的个数小于k了
                    {
                        if(node[r].nxt==n+1)break;//移动到最后也就结束了,不能再往后移动
                        cntr++,r=node[r].nxt;//右区间往后移动
                    }
                    if(cntl+cntr==k)//正好左右区间中有这么多数
                    {
                        int L=node[l].idx-node[node[l].pre].idx;//左边涉及的区间
                        int R=node[node[r].nxt].idx-node[r].idx;///右边涉及的区间
                        ans+=1ll*L*R*i;
                    }
                    l=node[l].nxt,cntl--;//左区间往后移动
                }
                node[node[idx[i]].pre].nxt=node[idx[i]].nxt;
                node[node[idx[i]].nxt].pre=node[idx[i]].pre;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    差分隐私 differential privacy privSQL ||sql query ||sql查询系统||PrivateSQL:A Differentially Private SQL Query Engine论文笔记
    分冶法解决大整数相乘 最近对问题
    数论 矩阵交集
    STl 优先队列 C++
    备份mysql函数和存储过程
    Idea 注解模板
    excel导出
    帆软常用小技巧
    js + java文件下载
    try/finally
  • 原文地址:https://www.cnblogs.com/nanfenggu/p/7900084.html
Copyright © 2011-2022 走看看