Codeforces #261 (Div. 2) D. Pashmak and Parmida's problem(数据结构:离散化+线段树+逆序数+二分查找)

D. Pashmak and Parmida's problem
time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Parmida is a clever girl and she wants to participate in Olympiads this year. Of course she wants her partner to be clever too (although he's not)! Parmida has prepared the following test problem for Pashmak.

There is a sequence a that consists of n integers a1, a2, ..., an. Let's denote f(l, r, x) the number of indices k such that:l ≤ k ≤ r and ak = x. His task is to calculate the number of pairs of indicies i, j (1 ≤ i < j ≤ n) such thatf(1, i, ai) > f(j, n, aj).

Help Pashmak with the test.

Input

The first line of the input contains an integer n (1 ≤ n ≤ 106). The second line contains n space-separated integersa1, a2, ..., an (1 ≤ ai ≤ 109).

Output

Print a single integer — the answer to the problem.

Sample test(s)
input
7
1 2 1 1 2 2 1
output
8
input
3
1 1 1
output
1
input
5
1 2 3 4 5
output
0



感觉这道题太凶残了,题目要求求对任意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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值