CodeForces -Codeforces Round #496 (Div. 3) E2. Median on Segments (General Case Edition)

参考:http://www.cnblogs.com/widsom/p/9290269.html

传送门:http://codeforces.com/contest/1005/problem/E2

题意:求一段数列中,取其中中位数为m的子序列个数有几个;

思路:首先我们可以先求出——序列中大于等于 m的数占多数的子序列——有多少个。然后,再求出序列中大于等于m+1的数占多数的子序列有多少个。

前面序列的个数减去后面的序列个数,就是答案。

显然这两个个数的求法是一样的。具体来说,

因为要计算区间的大于等于m个数是否占多数,把大于等于m的记为1,小于的记为-1;

计算前缀和cnt[i]。

枚举右端点t, 1 ~ t 间大于m的个数就是cnt[ t ],这个时候,找到左端点q个数,要求 1 ~ q 的cnt [ q ]小于cnt[ t ],   这个q的个数就是对应右端点为 t 时子序列的个数,加到ans中。

怎么找到q的个数,如果从1 ~ i枚举是会超时的,这时候就出了树状数组,感觉前缀和用树状数组很方便。

把cnt[q] 加上  n  再add进树状数组中。

这里有个细节就是,开始的时候要add(n+1),因为还要考虑左端点为0的情况。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;

int n,m;
const int maxn = 2e5+9;
int a[maxn];
ll sum[maxn*2],cnt[maxn*2];
int lowbit(int x){
    return x & (-x);
}

void add(int x){
    for(int i=x; i<=2*n; i += lowbit(i)){
        sum[i]++;
    }
}

ll getsum(int x){
    ll res = 0;
    for(int i=x; i>0; i-=lowbit(i)){
        res += sum[i];
    }
    return res;
}

ll solve(int x){
    
    memset(cnt,0,sizeof(cnt));
    memset(sum,0,sizeof(sum));


    for(int i=1; i<=n; i++){
            cnt[i] = cnt[i-1] + (a[i]>=x?1:-1);        
    }  
    ll ans = 0;
    add(n+1);           
    for(int i=1; i<=n; i++){
        ans += getsum(cnt[i]+n);        
        add(cnt[i]+n+1);
    }
    return ans;
}

int main(){
    scanf("%d%d", &n, &m);
    
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
    }

    printf("%I64d\n", solve(m) - solve(m+1));

    return 0;
}
1005E2

 

posted @ 2018-07-13 21:16  ckxkexing  阅读(160)  评论(0编辑  收藏  举报