我们假设, 一次可以击打任意多相邻连续的红球,也可以只击打一个球。 并且红球既不会落袋,也不会相
互发生碰撞,而只是停留在原处。每次击打时候,要想“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{sumleft[ j ight] -sumleft[ i ight] }{j-i} >m)的区间的个数,这里规定(j>i)
我们变形一下便会得到
(sumleft[ j ight] -sumleft[ i ight] >mj-mi(j>i))
(sumleft[ j ight] -mj >sumleft[ i ight] -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;
}