zoukankan      html  css  js  c++  java
  • BZOJ5125: [Lydsy1712月赛]小Q的书架【决策单调性优化DP】【BIT】【莫队】【分治】

    小Q有n本书,每本书有一个独一无二的编号,现在它们正零乱地在地上排成了一排。

    小Q希望把这一排书分成恰好k段,使得每段至少有一本书,然后把每段按照现在的顺序依次放到k层书架的每一层上去。将所有书都放到书架上后,小Q这才突然意识到它们是乱序的,他只好把每一层的书分别按照编号

    从小到大排序。排序每次可以在1单位时间内交换同一层上两本相邻的书。

    请写一个程序,帮助小Q计算如何划分这k段,且如何交换这些书,使得总交换次数最少。

    Input

    第一行包含两个正整数n; k(1≤n≤40000;1≤k≤min(10; n))。

    第二行包含n个互不相同的正整数a1,a2,..., an(1≤ai≤n),分别表示地面上每本书的编号。

    Output

    输出一行一个整数,即最少的总交换次数。

    Examples

    stdin

    6 3

    4 3 6 2 5 1

    stdout

    1

    Notes

    ([4,3,6][2,5][1])划分,需要排序1 + 0 + 0 = 1次。


    思路

    分成k段,最小化逆序对个数之和

    非常套路了吧

    决策单调性非常显然

    那么就对于每一层进行分治

    然后中间怎么维护逆序对个数?

    可以用莫队+树状数组

    因为首尾删/加数的逆序对个数是很好维护的(bit查一下就可以啦)

    然后就很简单了

    几分钟就写完了。。编译过了就A了


    //Author: dream_maker
    #include<bits/stdc++.h>
    using namespace std;
    //----------------------------------------------
    typedef pair<int, int> pi;
    typedef long long ll;
    typedef double db;
    #define fi first
    #define se second
    #define fu(a, b, c) for (int a = b; a <= c; ++a)
    #define fd(a, b, c) for (int a = b; a >= c; --a)
    #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
    const int INF_of_int = 1e9;
    const ll INF_of_ll = 1e18;
    template <typename T>
    void Read(T &x) {
      bool w = 1;x = 0;
      char c = getchar();
      while (!isdigit(c) && c != '-') c = getchar();
      if (c == '-') w = 0, c = getchar();
      while (isdigit(c)) {
        x = (x<<1) + (x<<3) + c -'0';
        c = getchar();
      }
      if (!w) x = -x;
    }
    template <typename T>
    void Write(T x) {
      if (x < 0) {
        putchar('-');
        x = -x;
      }
      if (x > 9) Write(x / 10);
      putchar(x % 10 + '0');
    }
    //----------------------------------------------
    const int N = 1e6 + 10;
    int n, m, a[N];
    int nowl = 1, nowr = 0;
    ll res = 0, dp[N][12];
    int bit[N];
    
    void add(int x) {
      for (; x <= n; x += x & (-x)) ++bit[x];
    }
    
    void sub(int x) {
      for (; x <= n; x += x & (-x)) --bit[x];
    }
    
    int query(int x) {
      int result = 0;
      for (; x; x -= x & (-x)) result += bit[x];
      return result;
    }
    
    int query(int l, int r) {
      return query(r) - query(l - 1);
    }
    
    void move_step(int al, int ar) {
      while (nowr < ar) {
        ++nowr;
        res += query(a[nowr], n);
        add(a[nowr]);
      }
      while (nowl > al) {
        --nowl;
        res += query(1, a[nowl]);
        add(a[nowl]);
      }
      while (nowr > ar) {
        sub(a[nowr]);
        res -= query(a[nowr], n);
        --nowr;
      }
      while (nowl < al) {
        sub(a[nowl]);
        res -= query(1, a[nowl]);
        ++nowl;
      }
    }
    void solve(int l, int r, int ql, int qr, int k) {
      if (l > r) return;
      int mid = (l + r) >> 1, pos = mid;
      fu(i, ql, min(qr, mid - 1)) {
        move_step(i + 1, mid);
        if (dp[i][k - 1] + res < dp[mid][k]) {
          dp[mid][k] = dp[i][k - 1] + res;
          pos = i;
        }
      }
      solve(l, mid - 1, ql, pos, k);
      solve(mid + 1, r, pos, qr, k);
    }
    int main() {
    #ifdef dream_maker
      freopen("input.txt", "r", stdin);
    #endif
      Read(n), Read(m);
      fu(i, 1, n) Read(a[i]);
      fu(i, 1, n)
        fu(j, 0, m) dp[i][j] = INF_of_ll;
      dp[0][0] = 0;
      fu(i, 1, m) solve(1, n, 0, n, i);
      Write(dp[n][m]);
      return 0;
    }
    
  • 相关阅读:
    二进制、八进制、十进制、十六进制的转换
    loadrunner-检查点
    loadrunner-集合点
    loadrunner-事务
    软件测试分类总结
    《黑客与画家》读后感
    说两个我在工作中有价值的bug
    HTTP状态码
    Android开发学习——android与服务器端数据交互
    Android开发学习——Volley框架
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9911288.html
Copyright © 2011-2022 走看看