洛谷P2717 寒假作业
题目:https://www.luogu.org/problemnew/show/2717
题目背景
zzs和zzy正在被寒假作业折磨,然而他们有答案可以抄啊。
题目描述
他们共有n项寒假作业。zzy给每项寒假作业都定义了一个疲劳值Ai,表示抄这个作业所要花的精力。zzs现在想要知道,有多少组连续的寒假作业的疲劳值的平均值不小于k?
简单地说,给定n个正整数A1,A2,A3,...,An,求出有多少个连续的子序列的平均值不小于k。
输入输出格式
输入格式:
第一行两个正整数,n和k。
第二行到第n+1行,每行一个正整数Ai。
输出格式:
一个非负整数。
输入输出样例
3 2 1 2 3
4
说明
样例解释:共有6个连续的子序列,分别是(1)、(2)、(3)、(1,2)、(2,3)、(1,2,3),平均值分别为1、2、3、1.5、2.5、2,其中平均值不小于k的共有4个。
对于20%的数据,1<=n<=100;
对于50%的数据,1<=n<=5000;
对于100%的数据,1<=n<=100000;
对于100%的数据,1<=Ai<=10000,1<=k<=10000。
解析
20分的话,枚举区间挨个算吧(~ ̄▽ ̄)~
50分的话,算个前缀和,枚举区间挨个算吧(~ ̄▽ ̄)~
100分怎么办?(⊙ω⊙)
是不是什么奇葩的数据结构呢?Σ( ° △ °|||)︴
反正蒟蒻我没想出来-_-!
但,我可以乱搞啊(~ ̄▽ ̄)~。
感觉那个k很烦人,我们转化一下,让每个数减k。
这样就转化成求大于等于0的区间啦。
我们不是还用过前缀和吗?那我这地方也可以用一次啦。
设s[i]代表a[i]-k的前缀和,那么,
如果i<j,且s[i]<s[j],那么区间i+1~j便是合法的喽。
有没有熟悉,有没有想到什么?(⊙ω⊙)
对啦,求逆序对哦不正序对= ̄ω ̄=
求正序对的方法和求逆序对的方法是相似的喽(~ ̄▽ ̄)~
这道题就完成啦。
warning:为什么我总是比答案少啊-_-!
tips:这不完全是逆序对啦( ﹁ ﹁ ) ~→
你需要考虑单个元素啦( ﹁ ﹁ ) ~→
单个元素若比0大答案也要+1啦( ﹁ ﹁ ) ~→
warning:为什么我会wa-_-!
tips:开long long( ﹁ ﹁ ) ~→
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define maxn 100100 8 #define ll long long 9 int a[maxn],s[maxn]; 10 int tmp[maxn]; 11 int n,k; 12 ll ans; 13 void merge(int l,int m,int r){ 14 int i=l,j=m+1,k=l; 15 while (i<=m&&j<=r){ 16 if (s[i]<=s[j]){ 17 ans+=r-j+1; 18 tmp[k++]=s[i++]; 19 }else{ 20 tmp[k++]=s[j++]; 21 } 22 } 23 while (i<=m) tmp[k++]=s[i++]; 24 while (j<=r) tmp[k++]=s[j++]; 25 for (int gg=l;gg<=r;++gg){ 26 s[gg]=tmp[gg]; 27 } 28 } 29 void merge_sort(int l,int r){ 30 int m=(l+r)>>1; 31 if (l<r){ 32 merge_sort(l,m); 33 merge_sort(m+1,r); 34 merge(l,m,r); 35 } 36 } 37 int main(){ 38 scanf("%d%d",&n,&k); 39 for (int i=1;i<=n;++i){ 40 scanf("%d",&a[i]); 41 s[i]=s[i-1]+a[i]-k; 42 } 43 merge_sort(0,n); //一定不要忘了单个元素情况 44 printf("%lld",ans); 45 return 0; 46 }