求逆序對
阿新 • • 發佈:2020-08-17
求逆序對的常用方法(樹狀陣列,歸併排序,線段樹)
1.樹狀陣列
首先對陣列b[i]進行離散化處理,按價值從大到小排序得到位置陣列a[i],排序後用樹狀陣列維護,將a[i](數從大到小排序後的位置)依次加入樹狀陣列,然後依次查詢a[i]位置前面一位的數,答案相加即為逆序對個數。
例:洛谷P1908 逆序對
#pragma GCC optimize("Ofast") #pragma GCC target("avx,avx2,fma") #pragma GCC optimize ("unroll-loops") #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #include<string> #include<queue> #include<map> #include<stack> #include<iostream> #define INF 0x3f3f3f3f #define lowbit(a) ((a)&-(a)) typedef long long ll; typedef unsigned long long ull; using namespace std; int n,c[500005],a[500005],b[500005]; void update(int x,int y,int n) { for(int i=x;i<=n;i+=lowbit(i)) { c[i]=c[i]+y; } } ll getsum(int x) { ll ans=0; for(int i=x;i;i-=lowbit(i)) { ans+=c[i]; } return ans; } int cmp(int s1,int s2) { if(b[s1]==b[s2]) return s1>s2; else return b[s1]>b[s2]; } int main() { ll ans=0; cin>>n; for(int i=1;i<=n;i++) { scanf("%d",&b[i]); a[i]=i; } sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) { update(a[i],1,n); ans+=getsum(a[i]-1); } cout<<ans<<endl; }
2.歸併排序
如歸併過程中左邊大於右邊,則逆序對個數加一;
#pragma GCC optimize("Ofast") #pragma GCC target("avx,avx2,fma") #pragma GCC optimize ("unroll-loops") #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #include<string> #include<queue> #include<map> #include<stack> #include<iostream> #define INF 0x3f3f3f3f #define lowbit(a) ((a)&-(a)) typedef long long ll; typedef unsigned long long ull; using namespace std; int n,a[500010],c[500010]; long long ans; void msort(int b,int e)//歸併排序 { if(b==e) return; int mid=(b+e)/2,i=b,j=mid+1,k=b; msort(b,mid),msort(mid+1,e); while(i<=mid&&j<=e) if(a[i]<=a[j]) c[k++]=a[i++]; else c[k++]=a[j++],ans+=mid-i+1;//統計答案 while(i<=mid) c[k++]=a[i++]; while(j<=e) c[k++]=a[j++]; for(int l=b;l<=e;l++) a[l]=c[l]; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); msort(1,n); printf("%lld",ans); return 0; }
3.線段樹
與樹狀陣列原理類似,進行單點修改,查詢區間和;