您的位置:首页 > 其它

hdu 6058 Kanade's sum

2017-08-09 19:25 459 查看
题意:给你一个一个数列。问1到n所有子区间中第k大值得和是多少。

分析:可以美枚举每一个数,计算它可以做多少次第k大值。因为k很小,所以可以枚举一个数,找到它前面第k个比它大的数和后面第k个比它大的数,然后计算。找的时候可以用单调栈维护。

#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;

typedef long long ll;
const int maxn = 5e5 + 5;
ll a[maxn];
ll l[maxn], r[maxn];
stack<ll> s;
ll le[100], re[100];

int main(){
int T;
ll n, k;
scanf("%d", &T);
while(T--){
memset(l, 0, sizeof(l));
memset(r, 0, sizeof(r));
ll ans = 0;
scanf("%lld%lld", &n, &k);
while(!s.empty()) s.pop();
for(int i = 1; i <= n; i++){
scanf("%lld", &a[i]);
while(!s.empty() && a[i] > a[s.top()]){
s.pop();
}
if(s.empty())
l[i] = 0;
else l[i] = s.top();
s.push(i);
}
while(!s.empty()) s.pop();
for(int i = n; i >= 1; i--){
while(!s.empty() && a[i] > a[s.top()]){
s.pop();
}
if(s.empty())
r[i] = n + 1;
else r[i] = s.top();
s.push(i);
}
for(int i = 1; i <= n; i++){
memset(le, 0, sizeof(le));
memset(re, 0, sizeof(re));
int tot1 = 0, tot2 = 0;
int j = i - 1;
le[0]=i;
for(tot1 = 1; tot1 <= k; tot1++){
if(j == 0) {
break;
}else{
while(j > 1 && a[j] < a[i]){
j = l[j];
}
if(a[j] > a[i])
le[tot1] = j--;
else break;
}
}
j = i + 1;
re[0] = i;
for(tot2 = 1; tot2 <= k; tot2++){
if(j > n) {
break;
}else{
while(j < n && a[j] < a[i]){
j = r[j];
}
if(a[j] > a[i]) re[tot2] = j++;
else break;
}
}
tot1--, tot2--;
if(tot1 < k){
ll tl = le[tot1];
if(k - tot1 - 1 > tot2) continue;
if(k - tot1 - 1 == tot2){
ans += a[i] * (n - re[tot2] + 1) * tl;
}else ans += a[i] * (re[k - tot1] - re[k - tot1 - 1]) * tl;
}
for(int j = tot1 - 1; j >= 0; j--){
ll pos = k - j - 1;
ll tl = le[j] - le[j + 1];
/* if(j == 0) tl = 1;
//if(pos > tot2) break;
if(pos == 0){
ans += a[i] * tl;
}else*/
if(pos < tot2){
ans += a[i] * (tl * (re[pos + 1] - re[pos]));
}else if(pos == tot2){
ans += a[i] * (tl * (n - re[pos] + 1));
break;
}
}
}
printf("%lld\n", ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: