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;
}

原文地址:https://www.cnblogs.com/dream-maker-yk/p/9911243.html

时间: 2024-08-29 02:58:54

Codeforces 868F. Yet Another Minimization Problem【决策单调性优化DP】【分治】【莫队】的相关文章

CodeForces 868F Yet Another Minimization Problem(决策单调性优化 + 分治)

题意 给定一个序列 \(\{a_1, a_2, \cdots, a_n\}\),要把它分成恰好 \(k\) 个连续子序列. 每个连续子序列的费用是其中相同元素的对数,求所有划分中的费用之和的最小值. \(2 \le n \le 10^5, 2 \le k \le \min(n, 20), 1 \le a_i \le n\) 题解 \(k\) 比较小,可以先考虑一个暴力 \(dp\) . 令 \(dp_{k, i}\) 为前 \(i\) 个数划分成 \(k\) 段所需要的最小花费. 那么转移如下

决策单调性优化dp 专题练习

决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队列 : 在保证插入和查询的x坐标均具有单调性时可以使用 2.单调栈+二分:保证插入有单调性,不保证查询有单调性 3.分治+ 1 或 2:在每次分治时将\([l,mid]\)这段区间排序后插入,然后更新右区间\([mid+1,r]\)的答案 二.分治.单调队列维护有单调性的转移 (甚至还有分治套分治)

CodeForces - 868F Yet Another Minimization Problem

Description 给定一个长度为 \(n(n\le 10^5)\) 的数列,第 \(i\) 个数是 \(a_i\in[1,n]\) ,要求将其划分为 \(k(2\le k\le min(20,n))\) 段以后每段价值和最小. 定义一段的价值为该段相同数的数对个数. Solution 定义 \(calc(l,r)\) 为 \([l,r]\) 这一段的价值, \(dp[j][i]\) 为前 \(i\) 个数划分为 \(j\) 段的最小价值.那么显然有 \[ dp[j][i]=min\{dp[

题解——[NOI2009]诗人小G 决策单调性优化DP

第一次写这种二分来优化决策单调性的问题.... 调了好久,,,各种细节问题 显然有DP方程: f[i]=min(f[j] + qpow(abs(sum[i] - sum[j] - L - 1))); 其中f[i]代表到了第i个句子的最小答案 qpow用于处理^p sum为前缀和 (同时为了处理句子之间的空格问题,我们在统计前缀和的时候就默认在句子后面加一个空格, 然后在计算的时候,由于每一行只有最后一个不用加空格,直接减掉这个多加的空格即可获得正确长度) 首先我们可以打表发现是满足决策单调性的,

决策单调性优化dp

决策单调性: 对于一些dp方程,经过一系列的猜想和证明,可以得出,所有取的最优解的转移点(即决策点)位置是单调递增的. 即:假设f[i]=min(f[j]+b[j]) (j<i) 并且,对于任意f[i]的决策点g[i],总有f[i+1]的决策点g[i+1]>=g[i](或者<=g[i]) 那么,这个方程就具备决策单调性. 这个有什么用吗? 不懂具体优化方法的话确实也没有什么用.可能还是n^2的.只不过范围可能少了一些. 经典入门例题: Description: [POI2011]Ligh

[BZOJ4709][JSOI2011]柠檬 决策单调性优化dp

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4709 我好弱啊QAQ,网上dalao们的题解根本看不懂啊,折腾了几个小时,有一点明白了. 首先要把朴素dp方程退出来. ①题目中说每次从序列的左右选一端取,但是如果你真的照着题目说的这样做我也不知道会怎么样.事实上很明显不管怎么取,最终答案都只跟划分出的是哪几个区间有关.所以不妨从左端开始取. ②如果取一个区间,区间第一个贝壳的大小和最后一个贝壳的大小不一样,那么很明显可以去掉第一个或最

P3515 [POI2011]Lightning Conductor[决策单调性优化]

给定一序列,求对于每一个$a_i$的最小非负整数$p_i$,使得$\forall j \neq i $有$ p_i>=a_j-a_i+ \sqrt{|i-j|}$. 绝对值很烦 ,先分左右情况单独做.现在假设j都在i左边,则$p_i=max{a_j-a_i+ \sqrt{i-j}}=max{a_j+ \sqrt{i-j} }-a_i$.带根号,不易斜率优化,考虑证决策单调性. 假设最优决策为j,j之前的任意决策称之为$j'$,则有 $f[j]+\sqrt{i-j} \geqslant f[j']

hdu 2993 MAX Average Problem (斜率优化dp入门)

MAX Average Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5855    Accepted Submission(s): 1456 Problem Description Consider a simple sequence which only contains positive integers as

【CodeForces】868F. Yet Another Minimization Problem

原题链接 题目大意是有N个数,分成K段,每一段的花费是这个数里相同的数的数对个数,要求花费最小 如果只是区间里相同数对个数的话,莫队就够了 而这里是!边单调性优化边莫队(只是类似莫队)!而移动的次数和分治的复杂度是一样的! 这个时候就不能用单调栈+二分了,得用分治 分治的方法就是\(Solve(l,r,ql,qr)\)表示我想计算区间\([l,r]\)的答案,然后转移过来的区间在\([ql,qr]\) 暴力计算出\(f[mid]\)的值,找到最优转移点是\(k\),然后分成\(Solve(l,m