题目链接:http://codeforces.com/problemset/problem/459/D
题目大意:
输入n的数组序列,问有多少对< i , j >满足 x
【1,i】中 a【x】==a【i】的个数num_1, 大于y
【j,n】中a【y】==a【j】的个数num_2 n范围1e6
题目思路:
如果单求逆序对的话因为数据很大还要离散化,但是预处理出每一个i的前边有多少个等于a【i】,后边有多少个等于a【i】,那么结合题意,我就要求的是当前i,后边有多少个j的hou比这个 i 的 qian 小,hou和qian就是上边标红字的地方所分别预处理出来的变量。这个具体怎么用树状数组来求这个逆序对呢。
先从最简单的树状数组求逆序对来说,假如当前值是a【i】,那么就ask(a[i]-1);然后add(a【i】,1); 但是对于这个双数组的倒着的遍历,每次都要询问比他更后的,有多少个的hou比他的前小,所以ask改成ask(a[i].qian-1); add改成add(a[i].hou,1);
至于怎么预处理,只要正着来一遍map数个数记录qian,再反着来一遍记录hou就ok了
笔者能力有限,代码极丑。。
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#define ll long long
#include<map>
using namespace std;
const int MAXN=1e6+5;
struct Point
{
int data,qian,hou;
} C[MAXN];
ll ni[MAXN];
map<int,int>m,m2;
long long ask(int x)
{
long long ans=0;
for( ; x>0; x-=(x&-x))ans+=ni[x];
return ans;
}
int Max=0;
void add(int x,int y)
{
while(x<=Max){
ni[x]+=y;
x+=(x&-x);
}
}
int main()
{
memset(ni,0,sizeof(ni));
m.clear();m2.clear();
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&C[i].data);
}
for(int i=1; i<=n; i++)
{
m[C[i].data]++;
C[i].qian=m[C[i].data];
Max=max(Max,C[i].qian);
}
for(int i=n; i>=1; i--)
{
m2[C[i].data]++;
C[i].hou=m2[C[i].data];
Max=max(Max,C[i].hou);
}
long long ans=0;ll zero=0;
for(int i=n; i>=1; i--)
{
if(C[i].qian==0)continue;
if(C[i].qian!=1)
ans+=ask(C[i].qian-1);
ans+=zero;
if(C[i].hou!=0)
add(C[i].hou,1);
else zero++;
}
printf("%lld\n",ans);
}