您的位置:首页 > 其它

17 多校 3 - 1003 - Kanade's sum (HDU 6058)

2017-08-03 17:33 465 查看


Kanade's sum

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 2286    Accepted Submission(s): 943


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

 

题意:求所有区间中第k大的数之和

可以转化为求一个数 x 为第k大的区间有多少个,设为F(x)个 ,可得答案为∑F(x)*x。

把所有数按输入顺序用链表连接后,从小到大遍历所有的数,求出各自的F(x)值,每求一个删一个数使得链表中的数总是 ≥ x ,求F(x)的方法都差不多,都是比较暴力的方法直接来,从数 x 的位置开始往前找,找 k-1 个比 x 大的数记录左位置L,如果没有k-1个就再往后找记录右位置R,直到 [L,R] 区间内只有 k-1 个比 x 大的数,同时还要两边再往外拓展出去,向左下一个比 x 大的数位置和L位置之间的距离就是可以拓展出去的记为A,右边同理记为B,可得以这一区间内的k-1个比x大的数字可以组成A*B个区间,然后再将最左边的大于x的数去掉,最右边再加入一个,再进行上述操作,最后求和就得到了F(x)。

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int N = 500000+100;
int t,n,k,pos
;//pos 用来记录各个位置所对应的值
struct node
{
int pos,pre,next;//分别代表每个值所对应的位置、上一个点所对应的位置、下一个点所对应的位置
}a
;
void del(int x)//删除操作
{
int pre,nxt;
pre = a[x].pre;
nxt = a[x].next;
if(pre==-1||nxt==-1)
{
if(pre==-1&&nxt==-1)
return;
else if(pre==-1)
{
nxt = pos[nxt];
a[nxt].pre = -1;
}
else
{
pre = pos[pre];
a[pre].next = -1;
}
}
else
{
pre = pos[pre];
nxt = pos[nxt];
a[pre].next = a[nxt].pos;
a[nxt].pre = a[pre].pos;
}
}
long long f(int x)
{
int l,r,cnt=0;
l = r = a[x].pos;
while(1)
{
if(a[pos[l]].pre==-1||cnt==k-1)
break;
l = a[pos[l]].pre;
cnt++;
}
while(cnt<k-1)
{
if(a[pos[r]].next==-1)
break;
r = a[pos[r]].next;
cnt++;
}
long long ans = 0;
while(1)
{
int A = l - a[pos[l]].pre;
int B = a[pos[r]].next==-1?n:a[pos[r]].next;//要注意在该点之后没有比x大的点的情况
B = B - r;
ans += A * B;
l = a[pos[l]].next;
r = a[pos[r]].next;
if(l==-1||r==-1||l>a[x].pos)
break;
}
return ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(a,-1,sizeof a);
memset(pos,-1,sizeof pos);
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
{
int num;
scanf("%d",&num);
pos[i] = num;
a[num].pos = i;
a[num].pre = i-1;
a[num].next = i+1;
if(i+1==n)
a[num].next = -1;
}
long long ans = 0;
for(int i=1;i<=n-k+1;i++)
{
ans += f(i)*i;
del(i);
}
printf("%lld\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: