Meaningful Mean AtCoder - 2581 (树状数组+离散化)

Problem Statement

You are given an integer sequence of length Na= {a1,a2,…,aN}, and an integer K.

a has N(N+1)2 non-empty contiguous subsequences, {al,al+1,…,ar(1lrN). Among them, how many have an arithmetic mean that is greater than or equal to K?

Constraints

  • All input values are integers.
  • 1N2×105
  • 1K109
  • 1ai109
Input

Input is given from Standard Input in the following format:

N K
a1
a2
:
aN
Output

Print the number of the non-empty contiguous subsequences with an arithmetic mean that is greater than or equal to K.

Sample Input 1

3 6
7
5
7
Sample Output 1

5

All the non-empty contiguous subsequences of a are listed below:

  • {a1} = {7}
  • {a1,a2} = {7,5}
  • {a1,a2,a3} = {7,5,7}
  • {a2} = {5}
  • {a2,a3} = {5,7}
  • {a3} = {7}

Their means are 7619356 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 2

1 2
1
Sample Output 2

0
Sample Input 3

7 26
10
20
30
40
30
20
10
Sample Output 3

13

题意:给n个数字,现在求 平均数大于K的集合(非空),有多少个?

思路:枚举所有的区间  ( L,R 】 (左开右闭,为了使区间不重叠,不同时包含端点) 我们先计算出sum[ i ]数组,代表前i个数的和,那么(L,R 】区间的和为sum【R】-sum【L】,sum【R】-sum【L】>=k*(R-L);==>sum【R】- sum【L】>=K*R-K*L;==>sum【R】-K*R>=sum【L】-K*L;(0<=L<R<=n)(L等于0时,第一个数字才能包含在区间里)(在符合此式子即为满足题意的区间)  sum【R】-K*R  (0<=R<=n)可以看成一个整体,先预处理求出所有该式子的值,这里我用num【i】代表sum【i】-K*i,确定区间右端点R,寻找有多少个左端点L 符合 num【L】<=num【R】,这个用树状数组来解决该问题(求R前面有多少个num【L】小于他的num)。因为num【i】很大,所以先离散化,然后用树状数组nlogn处理。

(因为(0,R],才能表示从a[1]开始的数据,所以num[0],sum[0]也是有必要算上的)

代码:

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<algorithm>
#define N 200500
#define LL long long
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
LL sum[N],t[N];
LL tree[N];
int n,k,a,m,nn;
void add(int x)
{
    while(x<=nn)//nn为上限,要大于离散化后的最大的数字,
    {
        tree[x]++;
        x+=x&-x;//向上更新,树状数组核心代码。此处就不解释了,不会的先学树状数组。
    }
}
LL query(int x)
{
    LL num=0;
    while(x)
    {
        num+=tree[x];
        x-=x&-x;//向下求和
    }
    return num;
}
int main()
{
    mem(t,0);
    mem(tree,0);
    mem(sum,0);
    scanf("%d%d",&n,&k);
    nn=n+10;
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a);
        t[i]=sum[i]=sum[i-1]+a-k;//预处理出sum[i]数组(就是上面讲解的num数组)
    }//t[i]数组为离散化的辅助数组
    sort(t,t+n+1);//排序,排n+1个数字的序(包括了sum[0])
    m=unique(t,t+n+1)-t;//去重
    for(int i=0; i<=n; i++)
        sum[i]=lower_bound(t,t+m+1,sum[i])-t+1;//离散化
    LL ans=0;
    for(int i=0;i<=n;i++)
    {
        ans+=query(sum[i]);//查询有多少个小于sum[i]的数字
        add(sum[i]);//更新树桩数组。
    }
    printf("%lld\n",ans);
}







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值