1027 Larry and Inversions (35 分)
又要找工作開始刷題了(等大佬內推),這道題題意很簡單,給一個1到N的數字的隨機順序數組,若分別將所有的子數組逆序表示,求所有的逆序數。
暴力解法
暴力做法很簡單,只需先求總數組A的逆序數,在考慮子數組中的逆序數就好了。假設原數組的逆序數RV(0,N)爲NA,子數組大小爲N,總組合數爲N×(N-1)/2,子數組逆序數RV(i,j)爲M,那麼反向後逆序數爲N*(N-1)/2-M,和原來的差距爲N*(N-1)/2-M-M=N*(N-1)/2-2M,那麼解答爲NA+N(N-1)/2-2*M。求逆序數RV(i,j)的算法暴力解法(N2)遍歷即可。暴力解法可以過兩個點。這樣每做一個子數組複雜度爲O(N2),總複雜度爲O(N4 )。
解法
關鍵點在於求解逆序數,求解逆序數時,可以看到RV(i,j),與RV(i,j-1)其實只差一個數字。那麼如果我們每次求解RV(i,j)時,只把A[j]和A[i-j]作對比,記錄那些A[j]<A[i-j]的數,每次求解RV(i,j)最壞情況只需要O(N),那麼總複雜度則爲O(N3)。
但是這對於1000的數組來說還是太大,我們引進樹狀數組,數狀數組是一種查詢爲O(log(N))的數據結構,我們可以藉助它查詢A[i-j]中大於A[j]的個數而只需要O(log(N))的時間複雜度,那麼總時間複雜度爲O(N2log(N)),正解。
#include <vector>
#include <iostream>
using namespace std;
int dis[1002][1002];
class TreeArray{
public:
TreeArray(){arr=vector<int>(1002);};
void insert(int t){
while(t<1002){
arr[t]+=1;
t+=lowbit(t);
}
}
void init(){
for(int i=0;i<1002;i++){
arr[i]=0;
}
}
int getSum(int t){
int sum=0;
while(t>0){
sum+=arr[t];
t-=lowbit(t);
}
return sum;
}
inline int lowbit(int t){
return (t&-t);
}
vector<int> arr;
};
void init(){
for(int i=0;i<1001;i++){
for(int j=0;j<1001;j++)
dis[i][j]=0;
}
}
int main(){
TreeArray tr;
init();
int N;
cin>>N;
int temp;
vector<int> num;
for(int i=0;i<N;i++){
cin>>temp;
num.push_back(temp);
}
long long numReverse=0;
for(int i=0;i<num.size();i++){
dis[i][i]=0;
tr.init();
tr.insert(num[i]);
for(int j=i+1;j<num.size();j++){
if(num[i]>num[j]){
numReverse++;
}
else{
}
dis[i][j]=dis[i][j-1]+j-i-tr.getSum(num[j]);
tr.insert(num[j]);
}
}
for(int i=0;i<num.size();i++){
cout<<numReverse;
if(i!=num.size()-1)
cout<<" ";
long long change=0;
for(int j=i+1;j<num.size();j++){
change=((j-i+1)*(j-i))/2-2*dis[i][j];
cout<<change+numReverse<<" ";
}
}
}