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; }
skr