【set】【链表】hdu6058 Kanade's sum

f(l,r,K)表示区间l,r里面的K大值,问你所有连续子区间的f之和。

l(i)表示i左侧第一个比它大的数的位置,r(i)表示i右侧第一个比它大的数的位置。可以用set处理出来。

把数从大到小排序,依次插入。然后更新l(i),r(i),形成链形结构。

然后对于一个i,向左跳最多K次,将这些位置记录下来,然后向右跳最多K次,每个右侧的位置最多有一个左侧的位置合法。累计答案。

#include<cstdio>
#include<set>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
set<int>S;
typedef set<int>::iterator ITER;
ll ans;
int T,n,K,a[500010],b[500010],L[500010],R[500010];
int ls[500010],rs[500010];
bool cmp(const int &i,const int &j){
    return a[i]>a[j];
}
int main(){
//    freopen("1003.in","r",stdin);
//    freopen("1003.out","w",stdout);
    scanf("%d",&T);
    for(;T;--T){
        S.clear();
        ans=0;
        scanf("%d%d",&n,&K);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;++i){
            b[i]=i;
        }
        sort(b+1,b+n+1,cmp);
        for(int i=1;i<=n;++i){
            ITER it=S.lower_bound(b[i]);
            if(i>1 && it!=S.begin()){
                --it;
                L[b[i]]=*it;
                R[*it]=b[i];
            }
            else{
                L[b[i]]=-1;
            }
            it=S.upper_bound(b[i]);
            if(i>1 && it!=S.end()){
                R[b[i]]=*it;
                L[*it]=b[i];
            }
            else{
                R[b[i]]=-1;
            }
            int e1=0,e2=0;
            ls[0]=rs[0]=b[i];
            for(int j=b[i],l=1;l<=K && L[j]!=-1;j=L[j],++l){
                ls[++e1]=L[j];
            }
            if(e1<K){
                ls[++e1]=0;
            }
            for(int l=1,j=b[i];l<=K && R[j]!=-1;j=R[j],++l){
                rs[++e2]=R[j];
                if(e2-1+(ls[e1]==0 ? e1-1 : e1)>=K-1){
                    ans+=(ll)(ls[K-e2]-ls[K-e2+1])*(ll)(rs[e2]-rs[e2-1])*(ll)a[b[i]];
                }
            }
            if(e2<K){
                rs[++e2]=n+1;
            }
            if(rs[e2]==n+1 && e2-1+((e1==K || ls[e1]==0) ? e1-1 : e1)>=K-1){
                ans+=(ll)(ls[K-e2]-ls[K-e2+1])*(ll)(rs[e2]-rs[e2-1])*(ll)a[b[i]];
            }
            else if(K==1){
                ans+=(ll)(b[i]-(L[b[i]]==-1 ? 0 : L[b[i]]))*(ll)((R[b[i]]==-1 ? n+1 : R[b[i]])-b[i])*(ll)a[b[i]];
            }
            S.insert(b[i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}

【set】【链表】hdu6058 Kanade's sum

时间: 2025-01-01 14:57:56

【set】【链表】hdu6058 Kanade's sum的相关文章

hdu-6058 Kanade&#39;s sum

题意:略 思路:要我们求每个区间第K大数之和,其实可以转换为求多少个区间的第K大数是X,然后我们在求和就好了. 那么我们可以从小到大枚举所有可能成为第K大的数.为什么从小到大呢? 因为从小到大我们就略去了大小的比较了,后面我们维护的链表就要把这个值除去. /* gyt Live up to every day */ #include<cstdio> #include<cmath> #include<iostream> #include<algorithm>

hdu 6058 Kanade&#39;s sum(链表)

题目链接:hdu 6058 Kanade's sum 题意: 给你一个n个数的排列,问你全部区间第k大的总和为多少. 题解: 我们只要求出对于一个数x左边最近的k个比他大的和右边最近k个比他大的,扫一下就可以知道有几个区间的k大值是x. 我们考虑从小到大枚举xxx,每次维护一个链表,链表里只有>=x的数,那么往左往右找只要暴力跳kkk次,删除也是O(1)的. 时间复杂度:O(nk) 这题只要是知道能从小到大枚举就好办了. 1 #include<bits/stdc++.h> 2 #defi

HDU 6058 Kanade&#39;s sum(链表)

http://acm.hdu.edu.cn/showproblem.php?pid=6058 题意:找出所有区间第K大的值之和. 思路: 又有点贡献值的味道,就是考虑当前这个数贡献了几个区间. 然后往左和往右分别找大于当前数的k-1个数,这样就可以确定区间的个数,这要求我们从小到大找 并且找完之后需要删除这个数,用链表来维护. 删除元素的目的是为了加速,保证了当前查找的元素是最小值,所以只需要跳跃寻找k次就可以.具体看代码. 1 #include<iostream> 2 #include<

【链表】2017多校训练3 HDU 6058 Kanade&#39;s sum

acm.hdu.edu.cn/showproblem.php?pid=6058 [题意] 给定一个排列,计算 [思路] 计算排列A中每个数的贡献,即对于每个ai,计算有ni个区间满足ai是区间中的第k大,那么ai对答案的贡献就是ai*ni 以ai为起点,统计ai右边离ai最近的,比ai大的k个数的位置 同理统计左边的位置,组合得到答案 关键是得到比ai大的离ai最近的k个数的位置 因为是排列,所以每个数都不相等,可以记录每个数的位置,然后从小到大枚举ai,这样维护一个双向链表,保证链表中的数就是

6058 Kanade&#39;s sum 链表维护()

题意:给出元素为[1,n]的排列a,定义f[l,r,k]为区间[l,r]内第k大的元素.给出k,求 累加和(l=1~n,r~l~n)f[l,r,k] . n<=5e5,k<=min(80,n)k<=80 a[i]贡献: 枚举左边有p个比它大,右边要有k-1-p个比它大.如何处理出左/右边第p个比它大的位置?从最小的数开始处理,链表初始满链,维护一个链表,当处理x时,链表中只有比x大的数 #include <bits/stdc++.h> using namespace std;

HDU 6058 Kanade&#39;s sum —— 2017 Multi-University Training 3

Kanade's sum Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2512    Accepted Submission(s): 1045 Problem Description Give you an array A[1..n]of length n. Let f(l,r,k) be the k-th largest eleme

1-Two sum 2-Add two numbers

1.Two sum Given an array of integers, find two numbers such that they add up to a specific target number. The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please

leetcode2 Add Two Numbers题解

题的大概意思就是,输入两个列表,这两个列表是两个逆序的数,比如说1->2->4就代表421.然后将两个链表翻转后相加,存入列表中,同样按照逆序存入列表,将其返回,刚开始题意理解错了,WA了两次,题目给出的一组数据比较具有迷惑性,就是243+564与432+465的结果都是807,所以刚开始我以为输入的两个链表的数正序的,只需将结果翻转就可以了.其实这道题和大整数相加差不太多,只要考虑一下进位就没什么问题了.\ 第一版代码如下,比较繁琐,还有一些测试语句: #include <stdio.

多项式相加

#include <stdio.h> #include <stdlib.h> typedef struct PolyNode *PtrToNode; //定义链表节点,也就是多项式中的某一项: typedef struct PolyNode { int Coeff; int Exponent; PtrToNode Next; } PolyNode; typedef PtrToNode Polynomial; /************************************