神秘数字
题目背景
在公元XXXX年,侦探小明收到了组织的神秘信息,在Y市的某个地方有一道神秘的门。小明到达了那个门后,发现门上有一行字:“这个门需要密码才能解锁。”小明左找右找,终于找到了一张字条:“现在给你n个数,请你求出这n个数里面,有多少个连续的数的平均数大于某个给定的数M?注意:这个数可能会很大,请输出这个数对92084931取模的结果。最终的结果即是这个门的密码。”小明苦思冥想了半天,但始终找不到答案。于是他来求助于你。请你帮他解决这个问题。由于小明十分着急,他最多只能等1秒。
题目描述
给定n个数,请你帮助小明求出里面有多少个连续的数的平均数大于给定的某个数M.并将这个方案数输出。注意:这个数可能会很大,所以请输出这个数对92084931取模的结果。
输入格式
两行。第一行为两个数n和M。第二行为n个数。
输出格式
一行一个数,即问题的解对92084931取模的结果
把每一个数字减去m后,问题就转化为求数列A中多少个区间的和大于0;
显然会想到用前缀和维护;
所以就是求前缀和数组s[i],i前面有几个数比s[i]小;
跟逆序对没有关系;
直接权值线段树就可以解决;
代码:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define lson k<<1
#define rson k<<1|1
#define N 500100
#define M 200010
using namespace std;
const LL mod=92084931;
int n,m;
int z;
LL sum;
int s[M];//差值前缀和
int a[M],b[M];
struct Node{
int l,r,w;
}tree[M<<2];
inline void build(int k,int ll,int rr){
tree[k].w=0,tree[k].l=ll,tree[k].r=rr;
if(ll==rr) return;
int mid=(ll+rr)>>1;
build(lson,ll,mid);
build(rson,mid+1,rr);
}
inline int ask(int k){
if(tree[k].l==tree[k].r) return tree[k].w;
int s=0;
int mid=(tree[k].l+tree[k].r)>>1;
if(z<=mid) s+=ask(lson);//搜索左子树
else s+=ask(rson)+tree[lson].w;//搜索右子树,左子树直接相加
return s;
}
inline void add(int k){
if(tree[k].l==tree[k].r){
tree[k].w++;
return;
}
int mid=(tree[k].l+tree[k].r)>>1;
if(z<=mid) add(lson);
else add(rson);
tree[k].w=tree[lson].w+tree[rson].w;
}
int main(){
scanf("%d%d",&n,&m);
build(1,1,n);
int x;
for(int i=1;i<=n;i++){
scanf("%d",&x);
s[i]=x-m;
s[i]+=s[i-1];
a[i]=s[i];
b[i]=s[i];
}
sort(b+1,b+n+1);
int len=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){//离散化,有负数;
int pos=lower_bound(b+1,b+len+1,s[i])-b;
s[i]=pos;
}
for(int i=1;i<=n;i++){
z=s[i]-1;
sum=sum+(LL)ask(1);//查找小于等于s[i]-1的数;
if(a[i]>0) sum++;//相当于1到i的平均数大于m
sum%=mod;
z=s[i];
add(1);
}
cout<<sum<<endl;
return 0;
}