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

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


这个题就是 求一个区间的平均值 使这个平均值 大于等于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;
}

你可能感兴趣的:(#,线段树,树状数组)