传送门:点击打开链接
题意:给一个序列(n<=5000),求所有子区间的中位数之和。
思路:O(n^2logn)的方法太多太多了,这里我们思考如何用O(n^2)来做,说实话非常的巧妙
首先我枚举位置p为中位数,那么,我再创一个辅助数组,把位置p标记为0,把位置上的数比A[p]小的标记为-1,把位置上的数比A[p]大的标记为1,把位置上的数等于A[p]的且位置比p小的标记为-1,把位置比p大的标记为1
那么,假如原数列是1 2 3 4,我现在枚举p=2,那么得到的辅助数组就是-1 0 1 1,那么怎样的区间才是满足条件的呢?
当然是这个区间首先要包含位置p,且区间内的辅助标记之和等于0或1,0是奇数的情况,1是偶数的情况,可以自己写几组数据应该很快就能明白。
所以我们就变成了求这些子区间的个数,很明显如果我们用前缀和来维护,再用一个数组来统计前缀和出现的次数,很快就能统计出来。
#include<map> #include<set> #include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int>PII; const int P = 5e3 + 5; const int MX = 1e4 + 5; const int INF = 0x3f3f3f3f; int n, A[MX], B[MX], cnt[MX]; int solve(int p) { B[0] = 0; int t, ret = 0; memset(cnt, 0, sizeof(cnt)); for(int i = 1; i <= n; i++) { if(i == p) t = 0; else if(A[i] < A[p] || A[i] == A[p] && i < p) t = -1; else t = 1; B[i] = B[i - 1] + t; } cnt[P] = 1; for(int i = 1; i <= p - 1; i++) { cnt[B[i] + P]++; } for(int i = p; i <= n; i++) { ret += cnt[B[i] + P] + cnt[B[i] + P - 1]; } return ret; } int main() { //FIN; while(~scanf("%d", &n)) { for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); } LL ans = 0; for(int i = 1; i <= n; i++) { ans += (LL)A[i] * solve(i); } printf("%lld\n", ans); } return 0; }