JZOJ 5947.初音未來(miku)
阿新 • • 發佈:2020-07-27
題目大意
維護一個數列 \(a_n\),\(m\) 次操作,每次對區間 \([l..r]\) 進行升序排序
求最後詢問區間 \([L..R]\),輸出 \(a_L,a_{L+1},···,a_{R}\)
思路
首先很容易想到暴力,這題暴力太好打了!!!
然而我們需要正解
於是有了後文
我們發現排序一段區間如果用氣泡排序的話就要 \(O(S^2)\),其中 \(S\) 為區間大小
進而挖掘氣泡排序的本質,如果 \(a[i]>a[i+1]\) 的話兩數就要交換(本題需升序,故符號為大於,降序反之)
發現我們在進行交換時做了沒必要的比較
如果能直接找到形如 \(a[i]>a[i+1]\)
那麼,對於一個待排序區間,只有找到形如 \(a[i]>a[i+1]\) 的兩個數直接交換,一直到區間沒有這對數,那麼排序就完成了
下次對於已經交換過的一對相鄰的數是不會再交換的,即不會再花費時間
那麼總的複雜度就是 \(O((n^2+m)\log n)\) 的
可以接受
用線段樹維護,初始時全為極大數(例如 \(0x3f3f3f3f\)),根據氣泡排序的交換順序,找到最靠左的一對數交換
所以我們如果發現形如 \(a[i]>a[i+1]\) 的兩個數,就把把線段樹 \(i\) 位置的值改成 \(i\),然後維護一個 \(min\)
具體考慮,就是對於一個操作,我們找到最靠左的一對形如 \(a[i]>a[i+1]\) 的兩個數直接交換,考慮交換後 \(a[i]\) 對 \(a[i-1]\) 的影響和 \(a[i+1]\) 對 \(a[i+2]\) 的影響。如果又產生了形如 \(a[i]>a[i+1]\) 的一對數,我們把線段樹 \(i\) 位置的值改成 \(i\),直到再無這種數。當然兩數交換後我們要把線段樹 \(i\) 位置的值改成 \(0x3f3f3f3f\)
\(Code\)
#include<cstdio> #include<iostream> using namespace std; const int N = 1505; int n , m , L , R , a[N] , seg[N << 2]; inline void pushup(int k){seg[k] = min(seg[k << 1] , seg[k << 1 | 1]);} inline void build(int l , int r , int k) { if (l == r) { seg[k] = 0x3f3f3f3f; return; } int mid = (l + r) >> 1; build(l , mid , k << 1); build(mid + 1 , r , k << 1 | 1); pushup(k); } inline void change(int x , int v , int l , int r , int k) { if (l == r && l == x) { seg[k] = v; return; } int mid = (l + r) >> 1; if (x <= mid) change(x , v , l , mid , k << 1); if (x > mid) change(x , v , mid + 1 , r , k << 1 | 1); pushup(k); } inline int query(int x , int y , int l , int r , int k) { if (x <= l && r <= y) return seg[k]; int mid = (l + r) >> 1 ,res = 0x3f3f3f3f; if (x <= mid) res = min(res , query(x , y , l , mid , k << 1)); if (y > mid) res = min(res , query(x , y , mid + 1 , r , k << 1 | 1)); return res; } int main() { freopen("miku.in" , "r" , stdin); freopen("miku.out" , "w" , stdout); scanf("%d%d%d%d" , &n , &m , &L , &R); build(1 , n , 1); for(register int i = 1; i <= n; i++) { scanf("%d" , a + i); if (i > 1 && a[i - 1] > a[i]) change(i - 1 , i - 1 , 1 , n , 1); } int x , y; for(register int i = 1; i <= m; i++) { scanf("%d%d" , &x , &y); if (x == y) continue; while (1) { int l = query(x , y - 1 , 1 , n , 1); if (l == 0x3f3f3f3f) break; swap(a[l] , a[l + 1]); change(l , 0x3f3f3f3f , 1 , n , 1); if (l - 1 && a[l - 1] > a[l]) change(l - 1 , l - 1 , 1 , n , 1); if (l + 2 <= n && a[l + 1] > a[l + 2]) change(l + 1 , l + 1 , 1 , n , 1); } } for(register int i = L; i <= R; i++) printf("%d " , a[i]); }