Codeforces 题面传送门 & 洛谷题面传送门

学 whk 时比较无聊开了道题做做发现是道神题(

介绍一种不太一样的做法,不观察出决策单调性也可以做。

首先一个很 trivial 的 observation 是,如果 \(2^{k-1}>n\)​ 那么答案就是 \(n\)​,因为我们可以第 \(i\) 段放 \(2^{i-1}\) 个数(最后一段除外),这样每一段中,肯定只有形如 \((x,x)\) 的整数对会产生贡献,这样答案刚好取到下界 \(n\)。

我们设 \(dp_{i,j}\) 表示前 \(i\) 个数分成 \(j\) 段,它们的 \(f\) 值之和的最小值,显然 \(j\) 的上界只用取到 \(18\),因此我们计算出 \(dp_{i,j}\) 之后即可 \(\Theta(1)\) 地求出答案。至此,问题转化为如何求 \(dp_{i,j}\)。

考虑化简 \(c(l,r)\) 的表达式,一个非常浅显的性质是对于 \(\min(i,j)<l\) 的数对 \((i,j)\),必然有 \(\gcd(i,j)<l\),因此我们可以直接那拿总数 \(\dfrac{r(r+1)}{2}\) 减去 \(\gcd(i,j)\) 小于 \(l\) 的数对 \((i,j)\) 的个数,即如果我们设 \(g(n,k)=\sum\limits_{i=1}^n\sum\limits_{j=i}^n[\gcd(i,j)\le k]\),那么有 \(dp_{i,j}=\min\limits_{k}dp_{k,j-1}+\dfrac{i(i+1)}{2}-g(i,k)\)。而根据莫比乌斯反演的知识,有

\[\begin{aligned}
g(n,k)&=\sum\limits_{i=1}^n\sum\limits_{j=1}^n[\gcd(i,j)\le k]\\
&=\sum\limits_{d=1}^k\sum\limits_{i=1}^n\sum\limits_{j=i}^n[\gcd(i,j)=d]\\
&=\sum\limits_{d=1}^k\sum\limits_{i=1}^{n/d}\sum\limits_{j=i}^{n/d}[\gcd(i,j)=1]\\
&=\sum\limits_{d=1}^k\dfrac{1+\sum\limits_{p=1}^{n/d}\mu(p)\lfloor\dfrac{n}{dp}\rfloor^2}{2}
\end{aligned}
\]

考虑怎样维护之,也就是说怎样动态地对于一个决策点 \(k\),用个什么东西维护 \(dp_{k,j-1}-g(i,k)\) 的值,进而求出全局最小值。考虑线段树优化 dp。我们建立一棵线段树,并且希望,当我们动态地枚举到 \(i\) 时,线段树上下标为 \(k\) 的位置上的值就等于 \(dp_{k,j-1}-g(i,k)\),那么就有 \(dp_{i,j}=\dfrac{i(i+1)}{2}+\text{此时线段树上全局最小值}\)。

这又该怎样维护呢?我们发现这样一个性质:当 \(n\) 由 \(n-1\) 变到 \(n\) 时,只有那些 \(d\mid n\) 的 \(\sum\limits_{p=1}^{n/d}\mu(p)\lfloor\dfrac{n}{dp}\rfloor^2\) 会发生改变,也就是说,对于一个固定的 \(k\),如果我们预处理出了 \(sum_i=\sum\limits_{i=1}^n\mu(i)\lfloor\dfrac{n}{i}\rfloor\),那么我们可以在 \(\Theta(d(n))\) 的时间内计算出 \(g(n,k)-g(n-1,k)\)。具体方法就是,遍历所有 \(d\mid n\),如果 \(d\le k\),那么就会 \(g(n,k)\) 就会相较于 \(g(n-1,k)\) 产生 \(\dfrac{1+\sum\limits_{p=1}^{n/d}\mu(p)\lfloor\dfrac{n}{dp}\rfloor^2}{2}-\dfrac{1+\sum\limits_{p=1}^{(n-1)/d}\mu(p)\lfloor\dfrac{n-1}{dp}\rfloor^2}{2}\) 的增量。发现了这个性质以后就很好维护了,当我们扫描到 \(i\) 时遍历一遍所有 \(i\) 的因子 \(d\),计算出 \(\dfrac{1+\sum\limits_{p=1}^{i/d}\mu(p)\lfloor\dfrac{i}{dp}\rfloor^2}{2}-\dfrac{1+\sum\limits_{p=1}^{(i-1)/d}\mu(p)\lfloor\dfrac{i-1}{dp}\rfloor^2}{2}\),然后在线段树上执行一遍后缀减即可。

时间复杂度 \(n\ln n\log^2n\),较为卡常。

using namespace fastio;
const int MAXV = 1e5;
const int LOG_N = 18;
const int MAXC = MAXV << 4;
int pr[MAXV / 6 + 5], mu[MAXV + 5], prcnt = 0;
int smu[MAXV + 5]; ll sm[MAXV + 5];
bool vis[MAXV + 5];
int hd[MAXV + 5], val[MAXC + 5], nxt[MAXC + 5], item_n = 0;
void ins(int x, int y) {val[++item_n] = y; nxt[item_n] = hd[x]; hd[x] = item_n;}
void sieve(int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) mu[i] = -1, pr[++prcnt] = i;
for (int j = 1; j <= prcnt && pr[j] * i <= n; j++) {
vis[pr[j] * i] = 1; if (i % pr[j] == 0) break;
mu[pr[j] * i] = -mu[i];
}
}
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j += i)
ins(j, i);
for (int i = 1; i <= n; i++) smu[i] = smu[i - 1] + mu[i];
for (int i = 1; i <= n; i++) {
sm[i] = sm[i - 1];
for (int e = hd[i]; e; e = nxt[e]) {
int f = val[e];
sm[i] -= 1ll * mu[f] * (i / f - 1) * (i / f - 1);
sm[i] += 1ll * mu[f] * (i / f) * (i / f);
}
}
}
ll dp[LOG_N + 2][MAXV + 5];
struct node {ll mn, lz;} s[MAXV * 4 + 5];
void pushup(int k) {s[k].mn = min(s[k << 1].mn, s[k << 1 | 1].mn) + s[k].lz;}
void build(int k, int l, int r, int lv) {
s[k].lz = 0; if (l == r) return s[k].mn = 2 * dp[lv][l], void();
int mid = l + r >> 1; build(k << 1, l, mid, lv); build(k << 1 | 1, mid + 1, r, lv);
pushup(k);
}
void tag(int k, ll v) {s[k].lz += v; s[k].mn += v;}
void modify(int k, int l, int r, int p, ll v) {
if (l >= p) return tag(k, v), void();
int mid = l + r >> 1;
if (p <= mid) modify(k << 1, l, mid, p, v), tag(k << 1 | 1, v);
else modify(k << 1 | 1, mid + 1, r, p, v);
pushup(k);
}
int main() {
sieve(MAXV); memset(dp, 0x1f, sizeof(dp)); dp[0][0] = 0;
for (int i = 1; i <= MAXV; i++) dp[1][i] = 1ll * i * (i + 1) / 2;
for (int i = 2; i <= LOG_N; i++) {
build(1, 0, MAXV, i - 1);
for (int j = 1; j <= MAXV; j++) {
for (int e = hd[j]; e; e = nxt[e]) {
int f = val[e];
ll ori = ((j / f == 1) ? 0 : 1) + sm[j / f - 1];
ll cur = 1 + sm[j / f];
modify(1, 0, MAXV, f, ori - cur);
}
dp[i][j] = ((s[1].mn + s[1].lz) / 2) + 1ll * j * (j + 1) / 2;
// printf("%d %d %lld\n", i, j, dp[i][j]);
}
}
int qu; read(qu);
while (qu--) {
int n, k; read(n); read(k);
if (k > LOG_N) print(n, '\n');
else print(dp[k][n], '\n');
}
print_final();
return 0;
}

Codeforces 1603D - Artistic Partition(莫反+线段树优化 dp)的更多相关文章

  1. Codeforces Round #426 (Div. 2) D 线段树优化dp

    D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...

  2. D - The Bakery CodeForces - 834D 线段树优化dp···

    D - The Bakery CodeForces - 834D 这个题目好难啊,我理解了好久,都没有怎么理解好, 这种线段树优化dp,感觉还是很难的. 直接说思路吧,说不清楚就看代码吧. 这个题目转 ...

  3. BZOJ2090: [Poi2010]Monotonicity 2【线段树优化DP】

    BZOJ2090: [Poi2010]Monotonicity 2[线段树优化DP] Description 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k]. ...

  4. [AGC011F] Train Service Planning [线段树优化dp+思维]

    思路 模意义 这题真tm有意思 我上下楼梯了半天做出来的qwq 首先,考虑到每K分钟有一辆车,那么可以把所有的操作都放到模$K$意义下进行 这时,我们只需要考虑两边的两辆车就好了. 定义一些称呼: 上 ...

  5. 【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp

    题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a varian ...

  6. POJ 2376 Cleaning Shifts (线段树优化DP)

    题目大意:给你很多条线段,开头结尾是$[l,r]$,让你覆盖整个区间$[1,T]$,求最少的线段数 题目传送门 线段树优化$DP$裸题.. 先去掉所有能被其他线段包含的线段,这种线段一定不在最优解里 ...

  7. 洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$

    正解:线段树优化$dp$ 解题报告: 传送门$QwQ$ 难受阿,,,本来想做考试题的,我还造了个精妙无比的题面,然后今天讲$dp$的时候被讲到了$kk$ 先考虑暴力$dp$?就设$f_{i,j}$表示 ...

  8. 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点

    容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...

  9. 2021.12.08 P1848 [USACO12OPEN]Bookshelf G(线段树优化DP)

    2021.12.08 P1848 [USACO12OPEN]Bookshelf G(线段树优化DP) https://www.luogu.com.cn/problem/P1848 题意: 当农夫约翰闲 ...

随机推荐

  1. [对对子队]会议记录5.22(Scrum Meeting9)

    今天已完成的工作 梁河览 ​ 工作内容:修改第一到九关新手引导,修复关卡选择bug ​ 相关issue:优化初步导出版本 ​ 相关签入:fix:修改第一关到第九关的新手引导和地图场景的bug 马嘉 ​ ...

  2. [no_code][Alpha]事后分析

    $( "#cnblogs_post_body" ).catalog() 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们要解决的 ...

  3. 单片机STM32在开发中常用库函数详解

    1.GPIO初始化函数 用法: voidGPIO_Configuration(void) { GPIO_InitTypeDefGPIO_InitStructure;//GPIO状态恢复默认参数 GPI ...

  4. Android编译执行envsetup.sh,产生工具命令m、mm、mmm、mmma、tapas 、croot、cgrep、jgrep、 resgrep、godir

    一般来说编译一个sdk或者一个比较大的工程项目,第一步都是执行 envsetup.sh这个脚本,比如编译android,qt源码以及其他一些嵌入式的sdk. 而且执行的时候需要特别注意使用 sourc ...

  5. linux命令中find, which、whereis、locate,有什么区别?

    whatis 用于查询一个命令执行什么功能,并将查询结果打印到终端上 which 查看可执行文件的位置 whereis 查看文件的位置 man Linux提供了丰富的帮助手册,当你需要查看某个命令的参 ...

  6. max-points-on-a-line leetcode C++

    Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. ...

  7. 字符串匹配 ?kmp : hash

    给定一个模式串S,以及一个模板串P,所有字符串中只包含大小写英文字母以及阿拉伯数字. 模板串P在模式串S中多次作为子串出现. 求出模板串P在模式串S中所有出现的位置的起始下标. 输入格式 第一行输入整 ...

  8. Ambari 2.4 在 CentOS 7.4 因 TLS_1.2 协商内部错误导致注册失败

    问题背景 业务准备在天翼云上搭建一套线上环境,VM 操作系统版本为 CentOS Linux release 7.4.1708,但是在 ambari Web 管理页面上部署hadoop节点主机的时候, ...

  9. 全面!总结BQ系列阻抗跟踪电量计化学Chemical ID配置和Golden学习方法

    BQ系列阻抗跟踪电量计SOC最高能达到1%,功能强大,应用起来也比较复杂.不仅要配置好参数,匹配好化学ID,并且进行好Golden学习和相关测试.本文就讲述ID匹配,Golden学习和测试的终极方法流 ...

  10. 执行新程序 execve()

    新程序的执行 一:execve() 之所以叫新程序的执行,原因是这部分内容一般发生在fork()和vfork()之后,在子进程中通过系统调用execve()可以将新程序加载到子进程的内存空间.这个操作 ...