P1908 逆序對-線段樹題解
阿新 • • 發佈:2021-01-02
讀完題目,你的第一想法肯定是直接暴力就能過了。實際上在看完資料之後。。。QAQ。好吧,肯定不能暴力,那
麼現在就要去尋找它的特徵。根據它的特徵,我們可以分析出要用線段樹去做這道題。那麼當我們確定這個演算法時,我
們就要找下一個難點:如何建立這個樹?這裡就直接告訴你叭:首先,我們要根據大小將初始陣列排列,然後再根據原
來的順序再排回來,然後根據每一個數的大小對號入座,在入座的過程中我們計算在這個葉子節點的右邊有多少個已經
入座的元素,這時候就體現出來了線段樹的便捷:查詢效率為O(log n) 級別。然後使用sum分別記錄,最後輸出sum。
/*
ID: erictian2018
TASK:
LANG: C++
*/
#include <iostream>
#include <cstring>
#include <string>
#include <fstream>
#include <algorithm>
#include <queue>
#include <stack>
#include <iomanip>
#include <math.h>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <map>
#define inf 0x7ffffff
#define ll long long
//#include <windows.h>
//#include <bits/stdc++.h>
using namespace std;
/*
輸入:
6
5 4 2 5 3 1
輸出:
*/
ll scan() {
ll x=0;
ll flag=0;
char ch=getchar();
while(ch<'0'||ch>'9') {
ch=getchar();
if(ch=='-')
flag=1;
}
for(; ch>='0' &&ch<='9'; ch=getchar()) x=x*10+ch-'0';
if(!flag)
return x;
return -x;
}
ll n;
struct node{
ll l,r;
ll cnt;cnt是指l到r有多少個元素
}tree[2000010];
ll sum;/求有多少個逆序對
struct st{
ll num,rank,order;//rk: 排序後的編號
}a[500100];
//
bool cmp1(st a,st b){
if(a.num==b.num)
return a.order<b.order;
return a.num<b.num;
}
//
bool cmp2(st a,st b){
return a.order<b.order;
}
//
void build(ll l,ll r,ll k){
tree[k].l=l;
tree[k].r=r;
if(l==r)
return ;
ll mid=(l+r)/2;
build(l,mid,k*2);
build(mid+1,r,k*2+1);
}
//
ll ask(ll l,ll r,ll k){
if(l<=tree[k].l&&tree[k].r<=r)
return tree[k].cnt;
ll mid=(tree[k].l+tree[k].r)/2;
ll value=0;
if(l<=mid)
value+=ask(l,r,k*2);
if(r>mid)
value+=ask(l,r,k*2+1);
return value;
}
//
void change(ll l,ll r,ll k){
if(l==tree[k].l&&tree[k].r==r){
tree[k].cnt++;
sum+=ask(l+1,n,1);
return ;
}
ll mid=(tree[k].l+tree[k].r)/2;
if(l<=mid)
change(l,r,k*2);
if(r>mid)
change(l,r,k*2+1);
tree[k].cnt=tree[k*2].cnt+tree[k*2+1].cnt;
}
int main() {
//ofstream fout ("test.out");
//ifstream fin ("test.in");
n=scan();
for(ll i=1;i<=n;i++){
a[i].num=scan();
a[i].order=i;
}
//從小到大排序 如果有相同的數則下標較小的排在前面
sort(a+1,a+n+1,cmp1);
for(ll i=1;i<=n;i++)
a[i].rank=i;
//根據order的下標再排回來
sort(a+1,a+n+1,cmp2);
build(1,n,1);
for(ll i=1;i<=n;i++)
change(a[i].rank,a[i].rank,1);
cout << sum << endl;
return 0;
}