感觉这道题太凶残了,题目要求求对任意1<=i<j <=n 使得f(1, i, a[i]) > f(j, n, a[j])成立的i, jh对数
令f1[i] = f(1, i, a[i]), f2[i] = f(i, n, a[i])
则转化为f1[i] > f2[j] && i<j的个数
可以发现为逆序数形式,所以可以想到用线段树求解
但我也只想到了这里,,后面完全不知道该怎么处理了
参考别人的代码才知道这道题有多凶残。。。
我觉得这道题达到区域赛的难度了
题目的解决方法是先预处理求出f1, f2
再利用线段树求逆序数
而f1 f2的求法则要使用离散化+二分查找
这里线段树求逆序数的用法也比较神奇
之前都是对一个数组求逆序数,而这里是对两个求
下面代码的做法是对f2进行建树,每次把f1[i] update到树中
再query (f2[i]+1, R)求的即是f1[0] - f1[i-1]中大于f2[i]的数的个数
这里query的过程中L = f2[i]+1的原因是我们要找的是逆序数是大于关系,而不是大于等于关系
因为离散化的原因,保证数字在1-R之间排列,所以需要令L = f2[i]+1才可以满足大于关系
直接贴代码吧,如果感觉看不懂就在get_f1 get_f2中把f1 f2数组打印出来i就可以理解了
代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000100
#define LL long long
using namespace std;
int n, L, R;
int f1[MAXN], f2[MAXN], a[MAXN], b[MAXN];
int vis[MAXN], num[MAXN<<2], disper[MAXN];
int binary_search(int v, int l, int r) {
int m;
while(l < r) {
m = (l+r) >> 1;
if(disper[m] >= v)
r = m;
else l = m+1;
}
return l;
}
void get_f1(int k) {
int tmp;
for(int i=0; i<n; ++i) {
tmp = binary_search(b[i], 0, k-1);
++vis[tmp];
f1[i] = vis[tmp];
}
}
void get_f2(int k) {
int tmp;
for(int i=n-1; i>=0; --i) {
tmp = binary_search(b[i], 0, k-1);
++vis[tmp];
f2[i] = vis[tmp];
R = max(R, f2[i]);
}
}
void Push_up(int rt) {
num[rt] = num[rt<<1] + num[rt<<1|1];
}
void update(int L, int l, int r, int rt) {
if(l == r)
++num[rt];
else {
int m = (l+r) >> 1;
if(L <= m)
update(L, l, m, rt<<1);
else update(L, m+1, r, rt<<1|1);
Push_up(rt);
}
}
LL query(int L, int R, int l, int r, int rt) {
if(L<=l && r<=R) {
return num[rt];
}
int m = (l+r) >> 1;
LL ans = 0;
if(L <= m) ans += query(L, R, l, m, rt<<1);
if(m < R) ans += query(L, R, m+1, r, rt<<1|1);
return ans;
}
int main(void) {
scanf("%d", &n);
for(int i=0; i<n; ++i) {
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(a, a+n);
int k = 0;
disper[k++] = a[0];
for(int i=1; i<n; ++i) {
if(a[i] != a[i-1])
disper[k++] = a[i];
}
get_f1(k);
memset(vis, 0, sizeof(vis));
get_f2(k);
//线段树求逆序数
LL ans = 0;
for(int i=0; i<n; ++i) {
L = f2[i]+1;
ans += query(L, R, 0, R, 1);
L = f1[i];
update(L, 0, R, 1);
}
cout << ans << endl;
return 0;
}