这是道有趣的观察性质题,可惜我没有脑子。
看到这个 dp 形式就非常整体二分,所以它就是整体二分(雾
我们先令 \(c(i,j)\) 表示 \(i\le x<y\le j,\gcd(x,y)\ge i\) 的数量,输出时 \(+n\) 即可。\(c(i,j)\) 的值在一些基本的转化后变为 \(\sum\limits_{k=i}^j\sum\limits_{x=1}^k\varphi(k)\),所以 \(n\sqrt{n}\) 预处理后可以 \(O(\log n)\) 计算。
然后发现这个整体二分状态爆炸,而且一时没有通过常规手段的优化方法,就只有减少状态了。
容易注意到 \(c(i,j)\) 当 \(j\ge 2i-1\) 时为 \(0\)。接下来就是一个绝妙的性质,\(f(n,k)\) 当 \(k>\log n\) 时为 \(0\),证明显然。果然 CF 就是要见到 2 倍想 \(\log\)。
剩下的就不用多说了。时间复杂度 \(O(n\log^2 n+n\sqrt{n})\)。
总结:踩了这么多次坑怎么还是没有把两倍限制和 \(\log\) 联系起来的直觉啊。
#include <cstdio>
#include <algorithm>
#include <cstring>
#define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++)
char buf[100000], *p1, *p2;
inline int read() {
char ch;
int x = 0;
while ((ch = gc) < 48);
do x = x * 10 + ch - 48; while ((ch = gc) >= 48);
return x;
}
typedef long long ll;
ll dp[18][100005], phi[100005], p[100005][680];
int Prime[100005], lft[100005][680], len[100005], cnt;
bool mark[100005];
void init(int n) {
for (int i = 2; i <= n; ++ i) {
if (!mark[i]) Prime[++ cnt] = i, phi[i] = i - 1;
for (int j = 1; i * Prime[j] <= n && j <= cnt; ++ j) {
mark[i * Prime[j]] = true;
if (i % Prime[j] == 0) {phi[i * Prime[j]] = phi[i] * Prime[j]; break;}
phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
}
}
for (int i = 2; i <= n; ++ i) phi[i] += phi[i - 1];
for (int i = 1; i <= n; ++ i) {
for (int l = 1, r; l <= i; l = r + 1) {
r = i / (i / l);
lft[i][++ len[i]] = l;
p[i][len[i]] = 1ll * (r - l + 1) * phi[i / l];
}
lft[i][++ len[i]] = i + 1;
for (int j = len[i] - 1; j; -- j) p[i][j] += p[i][j + 1];
}
}
inline ll cost(int i, int j) {
int pos = std::lower_bound(lft[j] + 1, lft[j] + len[j] + 1, i) - lft[j];
return p[j][pos] + 1ll * (lft[j][pos] - i) * phi[j / i];
}
void solve(ll *f, ll *g, int l, int r, int x, int y) {
if (l > r) return;
if (x == y) {
for (int i = l; i <= r; ++ i) g[i] = f[x] + cost(x + 1, i);
return;
}
int mid = l + r >> 1, opt = 0;
ll now = cost(std::min(y, mid - 1) + 1, mid);
for (int i = std::min(y, mid - 1); i >= x; -- i) {
if (g[mid] > f[i] + now) g[mid] = f[i] + now, opt = i;
now += phi[mid / i];
}
solve(f, g, l, mid - 1, x, opt), solve(f, g, mid + 1, r, opt, y);
}
int main() {
init(100000);
memset(dp, 0x3f, sizeof dp);
for (int i = 1; i <= 100000; ++ i) dp[1][i] = cost(1, i);
for (int i = 2; i <= 17; ++ i) solve(dp[i - 1], dp[i], i, 100000, i - 1, 99999);
int _ = read();
while (_ --) {
int n = read(), k = read();
if (k > 17) {printf("%d\n", n); continue;}
printf("%lld\n", dp[k][n] + n);
}
return 0;
}
标签:100005,p1,int,Partition,mid,CF1603D,Artistic,now,dp
From: https://www.cnblogs.com/stinger/p/16640739.html