您的位置:首页 > 其它

hdu 6058/2017多校联合第三场Kanade's sum(链表!)

2017-08-02 14:17 330 查看


Kanade's sum

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

Total Submission(s): 1456    Accepted Submission(s): 581


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

 

Source

2017 Multi-University Training Contest - Team 3

 

Recommend

liuyiding   |   We have carefully selected several similar problems for you:  6066 6065 6064 6063 6062 

 

Statistic | Submit | Discuss | Note

//题意:求左右区间的第K大的权值的和,不存在则为0
//思路:等价于求每个数字分别是多少区间的第K大,也就是可以枚举位置,然后在枚举左边有x个数比这个数要大,那么右边必须要有k-1-x个数比这个位置的数要大。
//因为序列是1~n的全排列,先记录每个数字出现的位置,我们维护一个链表,从最大值n开始枚举到1,每次把值插入到链表中,然后通过遍历链表用滑动窗口的方法达到O(n*(logn+k)),logn是用set纪录已经插入的坐标有哪些,然后可以在logn时间内找到第一个比当前插入坐标要大的坐标,达到修改链表的操作。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <string.h>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <list>
#include <bitset>
#include <stack>
#include <stdlib.h>
#define lowbit(x) (x&-x)
#define e exp(1.0)
//ios::sync_with_stdio(false);
//    auto start = clock();
//    cout << (clock() - start) / (double)CLOCKS_PER_SEC;
typedef long long ll;
typedef long long LL;
const int mod=1e9+7;
using namespace std;

//题意:求左右区间的第K大的权值的和,不存在则为0
//思路:等价于求每个数字分别是多少区间的第K大,也就是可以枚举位置,然后在枚举左边有x个数比这个数要大,那么右边必须要有k-1-x个数比这个位置的数要大。
//因为序列是1~n的全排列,先记录每个数字出现的位置,我们维护一个链表,从最大值n开始枚举到1,每次把值插入到链表中,然后通过遍历链表用滑动窗口的方法达到O(n*(logn+k)),logn是用set纪录已经插入的坐标有哪些,然后可以在logn时间内找到第一个比当前插入坐标要大的坐标,达到修改链表的操作。
const int maxn=500005;
int n;
int le[maxn],ri[maxn],vis[maxn];//left[i]表示第i个位置的前一个位置在哪
const int read()
{
char ch=getchar();
while(ch<'0' || ch>'9')
ch=getchar();
int x=ch-'0';
while((ch=getchar())>='0' && ch<='9')
x=x*10+ch-'0';
return x;
}
int main()
{
int T;
cin>>T;
while(T--)
{
ll ans=0;
int k;
scanf("%d%d",&n,&k);
set<int>s;
set<int>::iterator it;
for(int i=1;i<=n;i++)
{
int x=read();
le[i]=0;
ri[i]=n+1;
vis[x]=i;//标记每个数字所在的位置
}
if(k<1)cout<<"0"<<endl;
le[n+1]=0;
ri[n+1]=n+1;
s.insert(0);
s.insert(n+1);//默认两位第0位和第n+1位最大
for(int i=n;i>0;i--)
{
s.insert(vis[i]);
it=s.find(vis[i]);
int l,r;
it++;//找到第一个比当前位置大的位置
r=*it;
l=le[r];
le[r]=vis[i];
ri[vis[i]]=r;
ri[l]=vis[i];
le[vis[i]]=l;//插入链表
int now_le=vis[i];
for(int j=0;j<k && now_le;j++)
now_le=le[now_le];//左端点
int now_ri=now_le;
for(int j=0;j<k && now_ri!=n+1;j++)
now_ri=ri[now_ri];//右端点
for(int j=0;j<k;j++)
{
if(now_le==vis[i] || now_ri==n+1) break;
int next_le=ri[now_le];
int next_ri=ri[now_ri];
ans+=1ll*(next_le-now_le)*(next_ri-now_ri)*i;
now_le=next_le;
now_ri=next_ri;
}
}
cout<<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  2017多校联合