2017 Multi-University Training Contest
来源:互联网 发布:数据库分组查询原理 编辑:程序博客网 时间:2024/06/06 01:18
Give you an array A[1..n] of length n .
Letf(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 youk , 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 integersn ,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 Sample Output 30
Let
Specially ,
Give you
There are T test cases.
For each test case,there are only two integers
15 21 2 3 4 5
题目大意:给你一个长度为n的数列(里面的数为1~n全部都包括顺序未知)给你一个数k。求【r,l】区间内的第k大的数的总和为多少。
解题思路:正着去求不好求还容易超时,用x遍历1~n 那么就想着求有哪些区间内的第k大的数为x把区间的个数求出来然后乘以x最后所有的相加就是最后的答案。
先求x左边第k个比x大的数再求x的右边第k个比x大的数,然后先以x为区间的右边界这时x右边的可用的点为x与它右边第一个比他大的数左边可用的点就为它第k个比它大的数与第k-1个比它大的数之间的个数令他们相乘然后向右移每次都进行这样的操作最后就能求出答案。
#include<cstring>#include<cstdio>#include<string>#include<iostream>#define N 500005using namespace std;typedef long long LL;int num[N];int a[85],b[85];LL sum;int main(){ int t,i,j,n,cnta,cntb,k; scanf("%d",&t); while(t--) { sum=0; scanf("%d%d",&n,&k); for(i=0; i<n; i++) { scanf("%d",&num[i]); } for(i=0; i<n; i++) { cnta=cntb=1; int tp=num[i]; a[0]=b[0]=0; for(j=i-1; j>=0; j--) { if(cnta>k) { break; } if(num[j]>tp) { a[cnta++]=i-j; } } if(j<=0) { a[cnta]=i+1; } for(j=i+1; j<n; j++) { if(cntb>k) { break; } if(num[j]>tp) { b[cntb++]=j-i; } } if(j>=n) { b[cntb]=n-i; } /*for(int l=0; l<=cnta; l++) { printf("%d\n",a[l]); } for(int f=0; f<=cntb; f++) { printf("%d\n",b[f]); }*/ for(int ii=0; ii<cnta; ii++) { if(k-ii-1>=cntb) { continue; } int tp1=a[ii+1]-a[ii]; int tp2=b[k-ii]-b[k-ii-1]; sum+=(LL)num[i]*tp1*tp2; } } printf("%lld\n",sum); } return 0;}
(本博客借鉴http://blog.csdn.net/fanbaobao829/article/details/76548912?locationNum=2&fps=1)
另一种写法是用的链表,比较快
思路:
1,寻找某个数位置在其前面的k个比它大的数以及k个位置在其后面的比他大的数。
2,题意可以转化为求某个数在多少个区间中是第k大的数,然后将结果加起来。
3,从小到大枚举可能成为第k大的数,由于是从小到大的枚举,那么剩下的数一定都是大于等于他的,这样也就省去了大小的比较。枚举之后维护链表将其删除。
#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn=500009;int T,n,k,a[maxn],pos[maxn],pre[maxn],np[maxn];int s[maxn],t[maxn];long long ans=0;int main(){ scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pos[a[i]]=i;//记录位置 pre[i]=i-1;//链表前驱 np[i]=i+1;//链表后继 } ans=0; int s0=0,t0=0; for(int num=1;num<=n-k+1;num++)//枚举可能成为第k大的数 { int p=pos[num]; s0=0,t0=0; //寻找前后k个比它大的数 for(int i=p;i&&s0<=k+1;i=pre[i]) s[++s0]=i; for(int j=p;j<=n&&t0<=k+1;j=np[j]) t[++t0]=j; //首尾额外添加两个无限大的数 s[++s0]=0; t[++t0]=n+1; for(int i=1;i<=s0-1;i++) { int tmp=k-i+1; if(tmp<=t0-1&&tmp>0) ans+=(s[i]-s[i+1])*1LL*(t[tmp+1]-t[tmp])*num; } //从链表中将此数删除 int tpre=pre[p]; int tnp=np[p]; if(tpre) np[tpre]=tnp; if(tnp<=n) pre[tnp]=tpre; pre[p]=np[p]=0; } printf("%I64d\n",ans); } return 0;}
阅读全文
0 0
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- #2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- #2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- B/S学习之路—DOM(2)
- Qt ListView使用
- c语言sprintf的使用,可以用作itoa
- java基础4
- OLED原理,时序和操作
- 2017 Multi-University Training Contest
- x-powered-by 隐藏 php的版本号
- getElementById+比较运算符
- ubuntu下防火墙配置(转)
- NBUOJ 2383 泰拳之王(计数)
- VS(SQL Server一样)设置护眼背景色
- linux系统中如何进入退出vim编辑器,方法及区别
- spring activeMQ 整合(四): JMS 事务管理
- CommandBuffer.Blit BuiltinRenderTextureType.CameraTarget为空的问题