Luogu P1966 火柴排隊(樹狀陣列、離散化)
阿新 • • 發佈:2022-03-13
題目大意:
求使兩列數字相差距離最小的交換次數。
思路:
要想使得距離最小,必須讓 \(a\) 序列的第 \(k\) 大值與 \(b\) 序列的第 \(k\) 大值處在相同的位置上。
值域是 \(0≤ height \leq 2^{31} - 1\) ,考慮離散化獲得第 \(k\) 大的值。
在對 \(a\) 、\(b\) 序列離散化之後,考慮怎樣對 \(b\) 進行交換,使得第 \(k\) 大值相對應。
我們構造一個對映關係,使得 \(seq[a[i]] = i\) ,依照這個對應關係,可以得到 \(b\) 目前的對應關係 \(tb = seq[b[i]]\) 。
例如:
seq: 1 2 3 4
a: 1 3 4 2
b: 1 4 2 3
tb: 1 3 4 2
若要使得 \(a\) 、\(b\) 第 \(k\) 大處在相同的位置上,即讓 \(seq\) 和 \(tb\) 相等,由於 \(seq\) 是升序排列,則問題最終轉化成將原本亂的 \(tb\) 序列升序排列的最少交換次數。
這就是求逆序對的問題,使用樹狀陣列解決。
Code:
#include <bits/stdc++.h> using namespace std; using ll = long long; const double eps = 1e-6; const int N = 1e6 + 7; //#define N 10 const int INF = 0x3f3f3f3f; const int mod = 1000000007; //998244353 const int dir[8][2] = {0, 1, 0, -1, 1, 0, -1, 0,/* dir4 */ -1, -1, -1, 1, 1, -1, 1, 1}; ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); } ll powmod(ll a, ll b) { ll res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; } template <typename T> bool ckmin(T &a, const T &b) { return b < a ? a = b, 1 : 0; } template <typename T> bool ckmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; } #define debug(a) cerr << "## " << #a << " = " << a << endl; constexpr int P = 99999997; //998244353 // assume -P <= x < 2P int norm(int x) { if (x < 0) { x += P; } if (x >= P) { x -= P; } return x; } template<class T> T power(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; } struct Z { int x; Z(int x = 0) : x(norm(x)) {} int val() const { return x; } Z operator-() const { return Z(norm(P - x)); } Z inv() const { assert(x != 0); return power(*this, P - 2); } Z &operator*=(const Z &rhs) { x = ll(x) * rhs.x % P; return *this; } Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; } Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; } Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); } friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; } friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; } friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; } friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; } }; template <typename T> struct Fenwick { const int n; vector<T> a; Fenwick(int n) : n(n), a(n + 1) {} void add(int x, T v) { for (int i = x; i <= n; i += i & -i) { a[i] += v; } } T sum(int x) { T ans = 0; for (int i = x; i > 0; i -= i & -i) { ans += a[i]; } return ans; } T rangeSum(int l, int r) { return sum(r) - sum(l); } }; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n + 1), b(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } for (int i = 1; i <= n; i++) { cin >> b[i]; } vector<int> lsa(n); // 離散化a for (int i = 0; i < n; i++) { lsa[i] = a[i + 1]; } sort(lsa.begin(), lsa.end()); lsa.erase(unique(lsa.begin(), lsa.end()), lsa.end()); auto getid_a = [&](int x) { return lower_bound(lsa.begin(), lsa.end(), x) - lsa.begin() + 1; }; vector<int> seq(n + 1); // change a for (int i = 1; i <= n; i++) { seq[getid_a(a[i])] = i; } vector<int> lsb(n); // 離散化b for (int i = 0; i < n; i++) { lsb[i] = b[i + 1]; } sort(lsb.begin(), lsb.end()); lsb.erase(unique(lsb.begin(), lsb.end()), lsb.end()); auto getid_b = [&](int x) { return lower_bound(lsb.begin(), lsb.end(), x) - lsb.begin() + 1; }; vector<int> tb(n + 1); for (int i = 1; i <= n; i++) { tb[i] = seq[getid_b(b[i])]; } Fenwick<Z> fen(n); vector<Z> num(n + 1); for (int i = n; i >= 1; i--) { num[i] = fen.sum(tb[i]); fen.add(tb[i], 1); } Z ans = 0; for (int i = 1; i <= n; i++) { ans += num[i]; } cout << ans.val() << "\n"; return 0; }