P2804 神秘数字
福利
题意
一个长度为 的区间 ,求其中有多少个连续子序列的平均数大于 ?答案对 取模。
分析
首先我们知道:对于一个区间,如果每个数都减去 ,那么这个区间的平均数就会减少 ;同时,它的任意连续子区间的平均数都会减少 。
既然这样,我们只需要令整个区间都减少 ,于是问题就转化成了:
一个长度为 的区间 ,求其中有多少个连续子序列的平均数大于 ?答案对 取模。
既然一个连续子区间的平均数大于 ,那么这个连续子区间的和也大于 ,于是问题又简化了:
一个长度为 的区间 ,求其中有多少个连续子序列的和大于 ?答案对 取模。
求和部分显然可以用前缀和计算 来解决(特别地,),一个区间和 可以转化为 ,问题再次变形:
一个长度为 的区间 ,求其中有多少对 使 ?答案对 取模。
显然相当于 ,所以其实我们要求的就是顺序对(就是把逆序对反过来求)。
求顺序对的方法很多,这里提供一个值域分块做法,貌似比权值线段树好写一点。
代码
时间复杂度 ,空间复杂度 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=2e5+10;
const ll mod=92084931;
int n;
ll m,a[N],s[500],len,v[N],sum;
int discrete(int n,ll *a){
int b[N];
for(int i=1;i<=n;i++)
b[i]=a[i];
sort(b+1,b+n+1);
int tot=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
return tot;
}
ll ask(int w){
int k=w/len;
ll ans=0;
for(int i=0;i<k;i++)
ans=(ans+s[i])%mod;
for(int i=k*len;i<w;i++)
ans=(ans+v[i])%mod;
return ans;
}
void add(int w){
v[w]++;
s[w/len]++;
}
int main(){
n=read()+1;m=read();//把下标从2开始统计,这样方面在前面插入0
for(int i=2;i<=n;i++)
a[i]=read()-m+a[i-1];//把a数组直接当前缀和用
int tot=discrete(n,a);//离散化
len=sqrt(tot);
add(a[1]);//插入0
for(int i=2;i<=n;i++){
sum=(sum+ask(a[i]))%mod;
add(a[i]);
}
write(sum);
return 0;
}
本文作者:luckydrawbox
本文链接:https://www.cnblogs.com/luckydrawbox/p/18526590
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步