Codeforces Round #261 (Div. 2) D. Pashmak and Parmida's problem (树状数组求逆序数 变形)

D. Pashmak and Parmida's problem

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 that f(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 integers a1, 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个数字中a[i]的个数用f(1,i,ai)来表示,问所给一列数字中有多少对i,j满足f(1,i,ai)>f(j,n,aj)。

想了半天毫无头绪,树状数组练了两天仍然找不到思路……网上看了神牛的代码也是orz了。迷の思路。

点击打开链接 借鉴一下大神的说明。

我们可以先把f(1,i,a[i])和f(j,n,a[j])写出来,观察一下,例如样例1:

n=7

A  1  2  1  1  2  2  1

R  4  3  3  2  2  1  1

L  1  1  2  3  2  3  4

其中A为给定的数组,Rj为f(j,n,a[j]),Li为f(1,i,a[i])。

对每个Li,我们要统计的其实就是符合(j>i,且Rj<Li)的Rj的个数。就是这个Li右边有多少个比它小的Rj。

这样我们可以用树状数组,把Rj各个数的数量全存到树状数组里,例如这个样例就是4有1个,3有2个,2有2个,1有2个。然后从左到右遍历Li,每次从树状数组里删掉Rj,并且求sum(Li-1),也就是树状数组中1~Li-1的和,也就是比Li小的元素个数。

例如这个样例,到L3时,树状数组里记了2个1和2个2(1个4和2个3在之前被删掉了),L3=2,则sum(L3-1)=sum(2)=1的数量+2的数量=3。ans+=3。


我认为本题值得注意的是,上述说明中,存到树状数组里时,下标表示的是个数。比如样例中4有1个,3有2个,2有2个,1有2个,那么树状数组借助map存进去时就是对1,2,2,2这三个位置分别+1,即1的位置+1,2的位置+3。而每次删除Rj时也不必担心,因为从左遍历Li,Rj被删除时已经被利用过了。

学习树状数组两天了还是这么生疏,不过毕竟不是大神难以速成,说说对树状数组的心得:最近几题中利用树状数组的特性多在于求和与修改的便利,但我认为树状数组的真正优越性在于处理数据时对顺序要求很低。像本题计算小于Li的数字时,只需求和sum(Li-1)即可。而前面几题多也用到这种想法,同时修改的便利性对后效性有了很大支持,无需过多考虑父节点的问题。这些是我目前所利用到的树状数组的一些优势。

下面是代码:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;

int a[1000005],c[1000005];

map<int, int>x,y;

int lowbit(int x){
    return x&(-x);
}

void add(int x,int num){
    while (x<=1000005) {
        c[x]+=num;
        x+=lowbit(x);
    }
}

long long sum(int x){
    long long s=0;
    while (x) {
        s+=c[x];
        x-=lowbit(x);
    }
    return s;
}

int main(){
    int n;
    cin>>n;
        memset(c, 0, sizeof(c));
        memset(a, 0, sizeof(a));
        long long ans=0;
        for (int i=0; i<n; i++) {
            scanf("%d",&a[i]);
            x[a[i]]++;
            add(x[a[i]], 1);
        }
        for (int i=0; i<n; i++) {
            y[a[i]]++;
            add(x[a[i]],-1);
            x[a[i]]--;
            ans+=sum(y[a[i]]-1);
        }
        printf("%lld\n",ans);
    return 0;
}



你可能感兴趣的:(Codeforces Round #261 (Div. 2) D. Pashmak and Parmida's problem (树状数组求逆序数 变形))