求逆序对与本质不同的逆序对
题目
Description
给定一个序列a1,a2,…,an,如果存在i<j,a[i]>a[j],那么我们称之为逆序对,求逆序对的数目
Input
第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。
N<=10^5。Ai<=10^5
Output
两行,第一行为所有逆序对总数,第二行为本质不同的逆序对总数。
Sample Input
4
3
2
3
2
Sample Output
3 1
求有多少个在a前面的数比a小
https://www.cnblogs.com/wzx-RS-STHN/p/13193271.html
思路:
由题目可知,逆序对是满足,i<j ,a[i]>a[j] 的两个数,也相当于右边比a[i]小的数与a[i]都构成逆序对;
那么逆序对的个数,就是每一个数右边比它小的数的个数的和;
所以求逆序对就可以用树状数组;
那么什么是本质不同的逆序对呢?就是a[i] 和 a[j]组成的逆序对不与其他的逆序对相同;
比如 3 2 3 2
逆序对的个数有3个,
那么本质不同的逆序对就是3 2,也就只有一个;
这样我们可以用 ff[i] 记录i是否出现过;
这样可以再建一个树状数组,表示不重复的个数;
f[i] 记录i右边比它小的数的个数(并且不重复)同等于在树状数组中求左边比他小的数的个数;
通过这个树状数组求f[i]值;
如
3 2 3 2 1
首先逆循环 ,
读入1, ff[1]==0,1没有出现过,加入树状数组,ff[1]=1,f[1]=0; ans+=f[1] 树状数组:1
读入2,ff[2]==0,没有出现过,加入树状数组,ff[2]=1,f[2]=1; ans+=f[2]......树状数组:1 2
读入3,ff[3]==0............................................,ff[3]=1,f[3]=2; ans+=f[3]......树状数组:1 2 3
读入2,ff[2]==1,出现过,不加树状数组.......,f[2]=1; ans加上答案并且减去以前的f[i]值
(也就是减去以前包涵的ans,如3 3 2 ,这两个3都与2组成逆序对,重复了,所以要减去以前包涵的ans)
以前的f[i]值也等于1 ,ans+=1-1; 树状数组:1 2 3
读入3,ff[3]==1............................................,f[3]=2;..................... 以前的f[i]值等于2,ans+=2-2; 树状数组:1 2 3
所以最后的答案是3,本质不同的树状数组是 3 1,3 2,2 1;
代码:
#include<bits/stdc++.h> typedef long long ll; using namespace std; inline ll read() { ll a=0,ha=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') ha=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*ha; } ll n,m,mx=-1,sum[1000010],f[1000010],ff[1000010]; ll b[1000010],a1[1000010]; struct oh { ll v,num; }a[1000010]; inline ll lowbit(ll x) { return x&(-x); } inline void insert(ll x,ll y) { while(x<=n) { sum[x]+=y; x+=lowbit(x); } } inline void insert1(ll x,ll y) { while(x<=mx) { sum[x]+=y; x+=lowbit(x); } } inline ll findout(ll x) { ll ans=0; while(x) { ans+=sum[x]; x-=lowbit(x); } return ans; } inline ll cmp(oh a,oh b) { if(a.v==b.v) return a.num<b.num; else return a.v<b.v; } int main() { n=read(); for(ll i=1;i<=n;i++) { a[i].v=read(); a[i].num=i; a1[i]=a[i].v;//记下原数组 用来求本质不同的逆序对, mx=max(mx,a1[i]);//本质不同的逆序对离散化不会,我是大蒟蒻 } sort(a+1,a+n+1,cmp); for(ll i=1;i<=n;i++) b[a[i].num]=i;//离散化求逆序对 //cout<<endl; //for(ll i=1;i<=n;i++) // cout<<b[i]<<endl; ll ans=0,anss=0; for(ll i=n;i>=1;i--) { ans+=findout(b[i]); insert(b[i],1); }//逆序对的求法 //--------------以下求本质不同的逆序对,详解看思路------------------ memset(sum,0,sizeof(sum));//重置树状数组 for(ll i=n;i>=1;i--) { if(!ff[a1[i]])//如果没有记录过 { ll x=findout(a1[i]-1);//查找比他小的个数 anss+=x; f[a1[i]]=x; //记下右边有多少数比他小 ff[a1[i]]=1; //标记出现过了 insert1(a1[i],1);//加入不重复的树状数组 }//如果记录过了就不加树状数组 else { ll x=findout(a1[i]-1);//查找树状数组中有左边多少比它小的数 anss+=x-f[a1[i]];//ans减去之前包涵过的 f[a1[i]]=x; } } printf("%lld\n%lld\n",ans,anss); }