You are given an integer sequence of length N, a= {a1,a2,…,aN}, and an integer K.
a has N(N+1)⁄2 non-empty contiguous subsequences, {al,al+1,…,ar} (1≤l≤r≤N). Among them, how many have an arithmetic mean that is greater than or equal to K?
ConstraintsInput is given from Standard Input in the following format:
N K a1 a2 : aNOutput
Print the number of the non-empty contiguous subsequences with an arithmetic mean that is greater than or equal to K.
Sample Input 13 6 7 5 7Sample Output 1
5
All the non-empty contiguous subsequences of a are listed below:
Their means are 7, 6, 19⁄3, 5, 6 and 7, respectively, and five among them are 6or greater. Note that {a1} and {a3} are indistinguishable by the values of their elements, but we count them individually.
Sample Input 21 2 1Sample Output 2
0Sample Input 3
7 26 10 20 30 40 30 20 10Sample Output 3
13
这个题就是 求一个区间的平均值 使这个平均值 大于等于k。
这个题参考了别人的博客。点击打开链接
sum[i] 代表求和。
sum[r] - sum[l] 代表 (l r ] 这个区间,
(sum[r] - sum[l] ) / ( r - l ) >= k
sum[r] - k*r >= sum[l] - k * l
所以 读入的时候 预处理一下。
算出来的时候, sum[i] - k * i 这个值很大,不连续,还有可能是重复的。所以就需要离散化。
这里有两种离散的方法。
方法一:
求出来数之后,在顺便把每个数的位置记录一下。然后双关键字排序, 按处理过的数字从小到大排序,如果处理过的数字一样,那就按位置从小到大排序。
排序之后,位置不是从小到大的顺序了, 找位置中的 比当前数 小的数有多少个。 这个时候要注意,因为是左开右闭,所以要注意 0 的情况。 在求 树状数组的时候, 注意加上 0 的情况就可以。
方法二:
a[i] = b[i] = sum[i] - i *k ;
先排序一个,从小到大排序。然后从小到大给排序过的数 编号。这个编号就有可能重复了,相同的数一个编号。
这个是第一种方法。
#include
using namespace std;
#define mem(x,v) memset(x,v,sizeof(x))
#define low(x) (x & (-x))
#define ll long long
const int N = 300000;
int c[N],d[N],n,k;
ll b[N];
struct node{
int b;
ll a;
}p[N];
bool cmp(const node &a, const node &b){
if (a.a == b.a) return a.b < b.b;
return a.a < b.a;
}
void add(int x){
if (x == 0) {
c[x]++;
return;
}
while(x <= n){
c[x]++;
x += low(x);
}
return;
}
ll sum(int x){
ll m = c[0];
while(x > 0){
m += c[x];
x -= low(x);
}
return m;
}
int main() {
ll ans = 0;
int x;
scanf("%d%d",&n,&k);
for (int i = 1; i <= n; i++){
scanf("%d",&x);
p[i].a = p[i-1].a + x - k; //计算 x 求和 除以 k*i; 然后就是求比当前数小的、
p[i].b = i; //数很大,要离散化。要注意是 左开右闭,所以 0 也要注意。
}
sort(p,p + n + 1,cmp);
/* for (int i = 0; i <= n; i++)
d[p[i].b] = i; //两种方式都可以,这个是 在还原回去,p[i].b 就是他原来的位置。
for (int i = 0; i <= n; i++){
if (d[i]>0) ans += sum(d[i]);
add(d[i]);
}*/
for (int i= 0; i <= n; i++){
if (p[i].b > 0) ans += sum(p[i].b); //处理 0 的情况。
add(p[i].b);
}
printf("%lld\n",ans);
return 0;
}
第二种方法。
#include
using namespace std;
#define mem(x,v) memset(x,v,sizeof(x))
#define low(x) (x & (-x))
#define ll long long
const int N = 300000;
int c[N],n,k;
ll b[N],d[N];
map mp;
void add(int x){
while(x <= n + 1){
c[x]++;
x += low(x);
}
return;
}
ll sum(int x){
ll m = 0;
while(x > 0){
m += c[x];
x -= low(x);
}
return m;
}
int main() {
ll ans = 0;
int x;
scanf("%d%d",&n,&k);
for (int i = 1; i <= n; i++){
scanf("%d",&x);
d[i] = b[i] = b[i-1] + x - k; //计算 x 求和 除以 k*i; 然后就是求比当前数小的、
}
int tt = 1;
sort(b,b + n + 1);
for (int i = 0; i <= n; i++)
if (!mp[b[i]]) mp[b[i]] = tt++; //这里就是离散的方法。
for (int i= 0; i <= n; i++){
ans += sum(mp[d[i]]); //处理 0 的情况。
add(mp[d[i]]);
}
printf("%lld\n",ans);
return 0;
}