線段樹專題#5_蒟蒻訓練歷程記錄_HDU 1394 Minimum Inversion Number_單點更新+思維轉換
阿新 • • 發佈:2019-02-08
啊這道題思維比較巧妙,首先用線段樹單點更新從後往前求得每一位的逆序數個數,相加得出逆序數的總和。然後就是要思考的東西了:從一個序列轉換為另一個序列時,把陣列的第一個元素移到陣列尾部,那麼這個時候逆序數的變化是多少呢:-a[i]+(n-1-a[i]);為什麼呢,因為這時候所有的元素都在它的後面,把它從第一位移到最後那麼減少的逆序數自然是這個時候排在它後面而且比它小的元素個數,同理增加的逆序數就是排在它後面比它大的元素個數,然後求最值就好了。樓主因為腦殘忘記每次ans置零了WA了一發,大家不要學習ORZ~:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<vector> #include<map> #include<algorithm> #include<queue> #include<stack>//線段樹單點更新查詢 求逆序數 using namespace std; typedef long long ll; #define N 5100 int g[4*N]; int low[N]; int a[N]; int ans[N]; void init(int rt, int l, int r) { if(l == r) { g[rt] = 0; return; } int mid = (l+r)>>1; init(rt<<1, l, mid); init(rt<<1|1, mid+1, r); g[rt] = 0; } int find(int rt, int l, int r, int L, int R) { if(l >= L && r <= R) { return g[rt]; } int mid = (l+r)>>1; int k1 = 0, k2 = 0; if(mid >= L) { k1 = find(rt<<1, l, mid, L, R); } if(mid < R) { k2 = find(rt<<1|1, mid+1, r, L, R); } return k1+k2; } void Update(int rt, int l, int r, int x) { if(l == r && l == x) { g[rt]++; return; } int mid = (l+r)>>1; if(x <= mid) { Update(rt<<1, l, mid, x); g[rt]++; } if(x > mid) { Update(rt<<1|1, mid+1, r, x); g[rt]++; } } int main() { int t, ans1; while(~scanf("%d", &t)) { int ans1 = 0; memset(ans, 0, sizeof(ans)); memset(low, 0, sizeof(low)); for(int i = 1; i <= t; i++) { scanf("%d", &a[i]); a[i]++; } init(1, 1, t); for(int i = t; i >= 1; i--) { if(a[i] == 1) low[i] = 0; else low[i] = find(1, 1, t, 1, a[i]-1); ans1 += low[i]; Update(1, 1, t, a[i]); } //printf("%d\n", ans1); ans[0] = ans1; for(int i = 1; i <= t; i++) { ans[i] = ans[i-1]-a[i]+(t-a[i])+1; ans1= min(ans[i], ans1);//每次更新的時候,這個元素都是在陣列的第一個 //這個元素移到最後,則逆序數的變化為,減少了a[i]-1個,增加了(t-a[i]個) //則轉移方程為 ans[i] = ans[i-1]-(a[i]-1)+(t-a[i]); //printf("%d\n", ans[i]); } printf("%d\n", ans1); } return 0; }