zoukankan      html  css  js  c++  java
  • Codeforces 868F. Yet Another Minimization Problem【决策单调性优化DP】【分治】【莫队】

    LINK


    题目大意

    给你一个序列分成k段

    每一段的代价是满足((a_i=a_j))的无序数对((i,j))的个数

    求最小的代价


    思路

    首先有一个暴力dp的思路是(dp_{i,k}=min(dp_{j,k}+calc(j+1,i)))

    然后看看怎么优化

    证明一下这个DP的决策单调性:

    trz说可以冥想一下是对的就可以

    所以我就不证了

    (其实就是决策点向左移动一定不会更优)

    然后就分治记录当前的处理区间和决策区间就可以啦


    //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 = 1e5 + 10;
    const int M = 23;
    int n, m, a[N], cnt[N] = {0};
    ll nowl = 1, nowr = 0, res = 0;
    ll dp[N][M];
    void move_step(int al, int ar) {
      while (nowr < ar) {
        ++nowr;
        res += cnt[a[nowr]];
        ++cnt[a[nowr]];
      }
      while (nowl > al) {
        --nowl;
        res += cnt[a[nowl]];
        ++cnt[a[nowl]];
      }
      while (nowr > ar) {
        --cnt[a[nowr]];
        res -= cnt[a[nowr]];
        --nowr;
      }
      while (nowl < al) {
        --cnt[a[nowl]];
        res -= cnt[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);
      freopen("output.txt", "w", stdout);
    #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;
    }
    
  • 相关阅读:
    又学到的2个小技巧
    CSS3 简写animation
    CSS3 区别Transform,Transition,Animation一句话
    纠错【2012年5月9日 JQuery跑马灯(文字无缝向上翻动)(自已封装的第三个插件)】
    jQuery计算选中的文本字数,并弹出一个层(插件四)
    html5Canvas绘制线条closePath();
    html5循环方式绘制矩形
    jQuery过滤not()与filter();
    javascript在firebug调试时用console.log
    jQuery插件在光标定位的地方插入文字(插件5)
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9911243.html
Copyright © 2011-2022 走看看