C - Kanade's sum HDU - 6058 (任意区间第K大求和 + 双向链表)

版权声明: https://blog.csdn.net/nucleare/article/details/88911371

C - Kanade's sum

 HDU - 6058 

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

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

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

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

There are T test cases. 

1≤T≤101≤T≤10 

k≤min(n,80)k≤min(n,80) 

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

∑n≤5∗105∑n≤5∗105

Input

There is only one integer T on first line. 

For each test case,there are only two integers nn,kk on first line,and the second line consists of nn integers which means the array A[1..n]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

 建立双向链表并采用暴力枚举的思想,从数组中的最小的元素开始遍历他的左右边看分别有多少个比他大的数,这样我们就可以知道该元素可以有多少个第K大的组合可能,然后把这个元素从链表里删除,保证删除的数比其他数都小,所以删除这个元素并不会对其他元素产生影响,这保证答案是正确的,其中还要注意处理边界

这位大佬的思路讲的不错

https://www.cnblogs.com/fightfordream/p/7272100.html

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1e6 + 7;
int a[N];
int pos[N], pre[N], net[N]; 
int st1[N], st2[N];
int main() {
	int t;
	scanf ("%d", &t);
	while (t--) {
		int n, k;
		scanf ("%d %d", &n, &k);
		for (int i = 1; i <= n; ++i) {
			scanf ("%d", &a[i]);
		} 
		for (int i = 1; i <= n; ++i) {
			pos[a[i]] = i; //a[i] 在数组的位置 
		}
		for (int i = 1; i <= n; ++i) { //第i个数的上/下一个比他大的元素的位置 
			pre[i] = i-1, net[i] = i+1;
		}
		int len1 = 0, len2 = 0; 
		long long ans = 0;
		for (int i = 1; i <= n-k+1; ++i) { //核心,从1开始找,所有元素都比当前元素大
			//为什么i <= n-k+1 ? 因为最多只能有n-k+1个数有k-1个数比他大的,也就是最多只有n-k+1个数能当第k大 
			len1 = 0, len2 = 0;
			for (int j = pos[i]; j >= 1 && len1 <= k+1; j = pre[j]) { //往前找 
				st1[++len1] = j;
			}
			for (int j = pos[i]; j <= n && len2 <= k+1; j = net[j]) { //往后找 
				st2[++len2] = j;
			}
			st1[++len1] = 0, st2[++len2] = n+1; //处理边界 
			for (int j = 1; j <= len1-1; ++j) { //看k属于多少个区间 
				if (k - j + 1 <= len2-1 && k - j >= 0) //k为第k大时左边有n个比他大的元素则右边有k-n-1个比他大元素; 
					ans += (st1[j] - st1[j+1])*(st2[k-j+2] - st2[k-j+1])*(long long)i;
			}
			//删除这个元素,因为他一定比其他元素小,对其他元素组成第k大没影响:1, 2, 3, ...
			//双向链表 
			net[pre[pos[i]]] = net[pos[i]];
			pre[net[pos[i]]] = pre[pos[i]];
		}
		printf ("%lld\n", ans); 
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/nucleare/article/details/88911371