球——数学分析,模型构建
我们假设, 一次可以击打任意多相邻连续的红球,也可以只击打一个球。 并且红球既不会落袋,也不会相
互发生碰撞,而只是停留在原处。每次击打时候,要想“K到红球”,至少要击打一个红球,如果想一次击打
多个红球,那么击打 的红球必须是依次连续排列的。 如果一次“K到红球”所有红球的标号之和的平均数大于
母球的标号 \(M\),就获得了一 个“连击”。
现在请你帮帮 hs_black 计算总共能有多少种“连击”方案。注意:如果当前有标号为 \(1,2,3\) 的三种红球,母球标号为 \(0\),有如下 \(6\) 种获得“连击”方 案:\((1),(2),(3),(1,2),(2,3),(1,2,3)\)
输入格式
输入共有两行,第一行是\(N\),\(M\) (\(N⩽100000,M⩽10000\)) ,NN 表示台面 上一共有 \(N\) 个红球,\(M\) 表示
母球的标 号。 第二行是 \(N\) 个正整数,依次表示台面上 \(N\) 个红球的标号,所有标号均不超过 \(10000\)
输出格式
输出只有一个数,为“连击”的方案总数。
思路
先说一下暴力,维护一个前缀和,\(n^2\)的时间复杂度扫一遍就行,
说正解,学会手玩式子还是挺重要的
我们要求的就是满足\(\dfrac{sum\left[ j\right] -sum\left[ i\right] }{j-i} >m\)的区间的个数,这里规定\(j>i\)
我们变形一下便会得到
\(sum\left[ j\right] -sum\left[ i\right] >mj-mi(j>i)\)
\(sum\left[ j\right] -mj >sum\left[ i\right] -mi(j>i)\)
不妨把\(sum[j]-mj\)和\(sum[i]-mi\)分别看成单独的两项,即\(v[j],v[i]\)
问题就简化成了\(v[j]>v[i](j>i)\)
看到这个我们还是没法用我们已知的算法进行求解,我们看到这个形式不难会想到逆序对
只要我们再取一次反,不就变成了\(v[j]<v[i](j>i)\),也就是求逆序对的问题吗
归并求或者树状数组进行求解都是可以的
但是这个注意有个小细节,就是如果单独的一个数是大于\(m\)的,那么它也是满足条件的,
为了不漏掉这种情况,我们在数的最开始加一个值为0的数
那会不会对其他情况产生影响呢,答案是不会的,因为我们的\(v[]\)早已经处理好了
代码
就是归并排序求逆序对的写法
#include<bits/stdc++.h>
using namespace std;
const int N=200000;
int sum[N];
int n,m;
int a[N];
template <typename T> inline void read(T &x) {
x = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
}
long long res;
int q[N],tmp[N];
long long merge_sort(int q[],int l,int r)
{
if(l>=r)return 0;
int mid=(l+r)/2;
res=merge_sort(q,l,mid)+merge_sort(q,mid+1,r);
int k=0;
int i=l;
int j=mid+1;
while(i<=mid&&j<=r)
{
if(q[i]<=q[j]) tmp[k++]=q[i++];
else {
res+=(mid-i+1);
tmp[k++]=q[j++];
}
}
while(i<=mid)tmp[k++]=q[i++];
while(j<=r) tmp[k++]=q[j++];
for(int i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
return res;
}
int main()
{
read(n);
read(m);
for(int i=1;i<=n;i++)
{
int x;
read(x);
sum[i]=sum[i-1]+x;
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i]-i*m;
sum[i]=-sum[i];
}
//产生新的sum数组 产生方式就是sum[i]=-(sum[i]-mi)
merge_sort(sum,0,n);
cout<<res<<endl;
return 0;
}