题解 P2804 【神秘数字】
思考当前问题
我们得到数列a,要求多少个区间的和平均数大于m
那么我们把数列a每一个数减去m 得到数列A
那么如果数列A中某个区间的和如果大于0,原数列a中这个区间的平均数就大于m(这个还是好想的,平均数大于m,那么和就大于m乘以区间长度)
问题转化为求当前数列A中有多少个区间的和大于0
继续思考
我们求出A数列的前缀和数列S
那么一个区间A[i]...Aj的和大于0的要求就是
S[j]-S[i-1]>0
也就是
S[j]>s[i-1]
至此,问题已经非常清晰了
问题转化为求S[j]之前有多少个S[i]>S[j],把相对于每一个S[j]的答案个数相加就是ans
也就是求前缀和数列S的逆序对
注意 要追加S[0]=0,因为这个代表从开头开始的一个区间也要计算(否则会少算一些答案)
下面贴出归并排序求逆序对的代码 期望复杂度O(nlogn)
楼主上大学了 多年没上洛谷 看到这题评论区里有讨论关于顺逆序的问题 楼主的意思是 ”顺“ “逆” 是对于这个“序”而言的 可以认为是升序的顺序对,也可以理解是降序的逆序对
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int a[200010],sum[200010],ans,temp[200010];
//sum为前缀和
int n,m;
int merge(int l,int mid,int r){
int p1=l,p2=mid+1,k=l-1;
while(p1<=mid&&p2<=r){
if(sum[p1]<sum[p2]){
ans+=(mid-p1+1);
ans%=92084931;
temp[++k]=sum[p2];
p2++;
}
else if(sum[p1]>=sum[p2]){
temp[++k]=sum[p1];
p1++;
}
}
while(p1<=mid) temp[++k]=sum[p1++];
while(p2<=r) temp[++k]=sum[p2++];
for(int i=l;i<=r;i++)
sum[i]=temp[i];
}
void mergesort(int l,int r){
if(l<r){
int mid=(l+r)>>1;
mergesort(l,mid);
mergesort(mid+1,r);
merge(l,mid,r);
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i]-=m;
sum[i]=sum[i-1]+a[i];
}
mergesort(0,n);
printf("%d\n",ans);
return 0;
}