HDU 6058 Kanade'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 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

题目大意:给定数列A,A是1,2,...,n的一个排序,求数列中所有区间的第k小的数之和。

思路:对于1~n的某个数 i,向左向右分别数出 k 个比 i 大的数,求得第 k 小的数是 i 的最大区间。然后在这个最大区间里,从左往右每次取k个不小于 i 的数,每一次更新区间时加上前后两个区间的差值即可。

AC代码(借鉴了下网上的代码):

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<vector>
 5 #include<algorithm>
 6 #include<fstream>
 7 using namespace std;
 8 int a[500005];
 9 long long l[90], r[90];
10 int main()
11 {
12     int T,n,k;
13     //ifstream cin("ylq.txt");
14     cin>>T;
15     while(T--)
16     {
17         scanf("%d %d", &n, &k);
18         //cin>>n>>k;
19         for(int i=1;i<=n;i++) scanf("%d", &a[i]);
20         int t;
21         long long ans=0;
22         for(int i=1;i<=n;i++){
23             t=0;
24             l[t++]=i;
25             int j;
26             for(j=i-1;j>0&&t<k;j--){
27                 if(a[j]>a[i]){
28                     l[t++]=j;
29                 }
30             }
31             long long sum=0;
32             if(t==k)
33             {
34                 int tmp=1;
35                 for(;j>0;j--){
36                     if(a[j]<a[i]) tmp++;
37                     else break;
38                 }
39                 sum+=tmp;
40                 for(j=i+1;j<=n&&t>=0;j++){
41                     if(a[j]<a[i]) sum+=tmp;
42                     else{
43                         t--;
44                         if(t==0) break;
45                         tmp=l[t-1]-l[t];
46                         sum+=tmp;
47                     }
48                 }
49             }
50             else
51             {
52                 for(j=i+1;j<=n&&t<k;j++){
53                     if(a[j]>a[i]) l[t++]=j;
54                 }
55                 if(t==k)
56                 {
57                     sort(l, l+t);
58                     int tmp=l[0];
59                     int p=0;
60                     sum+=tmp;
61                     for(;j<=n&&p<t;j++){
62                         if(a[j]<a[i]) sum+=tmp;
63                         else{
64                             p++;
65                             if(l[p]>i) break;
66                             tmp=abs(l[p]-l[p-1]);
67                             sum+=tmp;
68                         }
69
70                     }
71                 }
72             }
73             ans+=(long long)(sum*a[i]);
74         }
75         cout<<ans<<endl;
76     }
77 }

HDU 6058 Kanade's sum —— 2017 Multi-University Training 3

时间: 2024-11-06 09:24:47

HDU 6058 Kanade's sum —— 2017 Multi-University Training 3的相关文章

【链表】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,这样维护一个双向链表,保证链表中的数就是

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<

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 6170 - Two strings | 2017 ZJUT Multi-University Training 9

/* HDU 6170 - Two strings [ DP ] | 2017 ZJUT Multi-University Training 9 题意: 定义*可以匹配任意长度,.可以匹配任意字符,问两串是否匹配 分析: dp[i][j] 代表B[i] 到 A[j]全部匹配 然后根据三种匹配类型分类讨论,可以从i推到i+1 复杂度O(n^2) */ #include <bits/stdc++.h> using namespace std; const int N = 2505; int t;

HDU 6168 - Numbers | 2017 ZJUT Multi-University Training 9

/* HDU 6168 - Numbers [ 思维 ] | 2017 ZJUT Multi-University Training 9 题意: .... 分析: 全放入multiset 从小到大,慢慢筛 */ #include <bits/stdc++.h> using namespace std; const int N = 125250; int n, s[N]; int a[N], cnt; multiset<int> st; multiset<int>::it

【set】【链表】hdu6058 Kanade&#39;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&g

hdu 6128 Inverse of sum(推公式)

题目链接:hdu 6128 Inverse of sum 题意: 给你n个数,问你有多少对i,j,满足i<j,并且1/(ai+aj)=1/ai+1/aj 在%p意义下. 题解: 不愧是高中生,推公式神题. 将式子通分化简后可得(ai2+aj2+ai*aj)%p=0. 然后两边同时将两边乘(ai-aj),化简可得(ai3-aj3)%p=0. 然后就可以用map记录一下个数,并且减掉ai==aj时不合法的情况就行了. 1 #include<bits/stdc++.h> 2 #define F

HDU 6057 Kanade&#39;s convolution

题目链接:HDU-6057 题意: 思路:先按照官方题解推导出下面的式子: 现在唯一的问题就是怎么解决[bit(x)-bit(y)=bit(k)]的问题. 我们定义\( F(A,k)_{i}=\left[ bit\left( i\right) =k\right] * A_{i} \),相当于把A.B.C分别按照bit划分成m+1个序列. 有如下公式: 同时我们发现\( C_k=F(C,bit(k)))_k \). 然后我们就可以搞出来啦! 代码: 1 #include<iostream> 2