【AtCoder Grand Contest 001F】Wide Swap [线段树][拓扑]
Wide Swap
Time Limit: 50 Sec Memory Limit: 512 MBDescription
Input
Output
Sample Input
8 3
4 5 7 8 3 1 2 6
Sample Output
1
2
6
7
5
3
4
8
HINT
Solution
首先,直接做难度系数较高,假设原序列为a,我们考虑设一个p,p[a_i] = i,即将题目中的权值与下标调换。
那么显然,要令a字典序最小,只要让p字典序最小即可。因为“权值小的尽量前”与“前面的权值尽量小”是一个意思。
现在操作转化为:相邻元素且权值差>=k的可以换顺序。
考虑一个暴力怎么做,显然是 i 与后面的所有 j 比,如果 abs(p_i - p_j) < k,则 i 和 j 的相对顺序就确定了, 连一条 p_i -> p_j 的边。
连边之后跑一边拓扑即可。
显然复杂度在于连边,因为这样暴力会有很多无用边。比如A->B, B->C, A->C,这条A->C显然无用。
我们考虑如何删掉 A->C 这种边。
倒着加入,显然 p_i 连向 (p_i-k, p_i)∪(p_i, p_i+k)。我们只需要分别连向两个区间中下标最小的那一个即可。用线段树维护一下区间最小值。
Code
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cmath> 8 #include<queue> 9 using namespace std; 10 typedef long long s64; 11 12 #define next nxt 13 const int ONE = 1000005; 14 const int INF = 2147483640; 15 16 int get() 17 { 18 int res = 1, Q = 1; char c; 19 while( (c = getchar()) < 48 || c > 57) 20 if(c == '-') Q = -1; 21 if(Q) res = c - 48; 22 while( (c = getchar()) >= 48 && c <= 57) 23 res = res * 10 + c - 48; 24 return res * Q; 25 } 26 27 int n, k; 28 int A[ONE], p[ONE]; 29 int next[ONE], first[ONE], go[ONE], Input[ONE], tot; 30 priority_queue <int, vector <int>, greater <int> > q; 31 32 int res = INF; 33 namespace Seg 34 { 35 struct power {int val;} Node[ONE * 4]; 36 void Build(int i, int l, int r) 37 { 38 Node[i].val = INF; 39 if(l == r) return; 40 int mid = l + r >> 1; 41 Build(i << 1, l, mid), Build(i << 1 | 1, mid + 1, r); 42 } 43 44 void Update(int i, int l, int r, int L, int x) 45 { 46 if(L <= l && r <= L) 47 { 48 Node[i].val = x; 49 return; 50 } 51 int mid = l + r >> 1; 52 if(L <= mid) Update(i << 1, l, mid, L, x); 53 else Update(i << 1 | 1, mid + 1, r, L, x); 54 Node[i].val = min(Node[i << 1].val, Node[i << 1 | 1].val); 55 } 56 57 void Query(int i, int l, int r, int L, int R) 58 { 59 if(L > R) return; 60 if(L <= l && r <= R) 61 { 62 res = min(res, Node[i].val); 63 return; 64 } 65 int mid = l + r >> 1; 66 if(L <= mid) Query(i << 1, l, mid, L, R); 67 if(mid + 1 <= R) Query(i << 1 | 1, mid + 1, r, L, R); 68 } 69 } 70 71 void Add(int u, int v) 72 { 73 Input[v]++, next[++tot] = first[u], first[u] = tot, go[tot] = v; 74 } 75 76 void Deal() 77 { 78 int now = 0; 79 for(int i = 1; i <= n; i++) 80 if(!Input[i]) q.push(i); 81 while(!q.empty()) 82 { 83 int u = q.top(); q.pop(); 84 A[u] = ++now; 85 for(int e = first[u]; e; e = next[e]) 86 { 87 int v = go[e]; 88 if(--Input[v] == 0) q.push(v); 89 } 90 } 91 for(int i = 1; i <= n; i++) printf("%d\n", A[i]); 92 } 93 94 int main() 95 { 96 n = get(), k = get(); 97 for(int i = 1; i <= n; i++) p[get()] = i; 98 99 Seg::Build(1, 1, n); 100 for(int i = n; i >= 1; i--) 101 { 102 res = INF, Seg::Query(1, 1, n, p[i] + 1, min(p[i] + k - 1, n)); 103 if(res != INF) Add(p[i], p[res]); 104 105 res = INF, Seg::Query(1, 1, n, max(1, p[i] - k + 1), p[i] - 1); 106 if(res != INF) Add(p[i], p[res]); 107 108 Seg::Update(1, 1, n, p[i], i); 109 } 110 111 Deal(); 112 }