http://acm.hdu.edu.cn/showproblem.php?pid=5701
这是这次百度之星初赛2B的第六题。之前白山云做过类似的题,省赛完回来,我看了一下大概就有这样的思路:首先枚举每一个数k,计算以这个数为中位数的区间个数。关键是计算中位数的处理方法,将所有大于k的数置为1,小于k的数置为-1,等于k的数置为0。于是区间中位数大于k的区间和就大于0,小于k的小于0,等于k的等于0。而且每个数都不等,所以区间和为0的个数就是中位数为k的个数。
关于计算区间和为0的个数。如果维护前缀和sum的个数。那么维护到i的时候,以i为右区间值的区间必然是sum(i)-sum(j),那么区间和为0就是前面有多少个前缀和为sum(i)。然后区间必须是奇数长度,所以需要对奇偶区间维护前缀和,此外,还需要对sum==0的情况特判。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #include <set> #include <map> #include <queue> #include <vector> #include <string> #define LL long long using namespace std; const int maxN = 8005; int n, a[maxN], b[maxN]; int cnt[2][maxN<<1]; int cal(int k) { for (int i = 0; i < n; ++i) { if (a[i] > k) b[i] = 1; else if (a[i] < k) b[i] = -1; else b[i] = 0; } int ans = 0, sum = 0; memset(cnt, 0, sizeof(cnt)); for (int i = 0; i < n; ++i) { sum += b[i]; ans += cnt[!(i&1)][sum+maxN]; if (sum == 0 && i%2 == 0) ans++; cnt[i&1][sum+maxN]++; } return ans; } void work() { for (int i = 0; i < n; ++i) { if (i) printf(" "); printf("%d", cal(a[i])); } printf(" "); } int main() { //freopen("test.in", "r", stdin); //freopen("test.out", "w", stdout); while (scanf("%d", &n) != EOF) { for (int i = 0; i < n; ++i) scanf("%d", &a[i]); work(); } return 0; }