最大公约数
寻求最大公约数是人民民主的真谛。
0x00 题目
题目描述
小 L 极其喜爱 gcd。
有一天,小 Z :小 L,问你个题。
小 Z:给出一个长度为 \(n\) 的序列 \(a\) 和一个整数 \(k\),把这个序列划分成连续的 \(k\) 段,每一段不能为空。
小 L:等等,\(\gcd\) 去哪里了。
小 Z:别急嘛,我们设 \(b_i\) 表示第 \(i\) 段的最大值,请问怎么划分该序列使得 \(\gcd(b_1, b_2, ..., b_{k - 1}, b_k)\) 的值最大。
小 L 沉思了一会,发现自己不会做,彻底地自闭了,于是他又向你来求助了,请你帮帮他。
输入格式
输入文件第一行包含两个整数 \(n, k\) ,表示序列的长度和需要划分的段数。
接下来第二行包含 \(n\) 个整数,表示这个序列。
输出格式
输出文件包含一个整数,表示你的答案。
样例输入
5 3
1 3 2 9 6
样例输出
3
样例解释
最优的划分可能有多种,这里给出一种最优的划分,将序列分成 \([1,3],[2,9],[6]\) 三段,其中 \(b_1=3,b_2=9,b_3=6\),所以答案为 \(\gcd(3,9,6)=3\)。
数据规模与约定
对于 \(100\%\) 的数据,\(1\le k\le n\le 10^3\),\(1\le k\le 5\times 10^2\),\(1\le a_i\le 10^{6}\)。
| 测试点编号 | \(n\) | \(k\) | \(a_i\) |
|---|---|---|---|
| \(1\sim 2\) | \(\le 5\) | / | / |
| \(3\sim 8\) | \(\le 10^2\) | / | / |
| \(9\sim 10\) | / | \(3\) | / |
| \(11\sim 12\) | / | / | \(\le 5\times 10^3\) |
| \(13\sim 20\) | / | / | / |
0x01 前置芝士
此题来源:\(2020/11/28\) 模拟赛 \(T2\)。
前置芝士:线性 \(dp\),贪心。
考场上降智了,其实这道题很水,把我和涵妹的思路合rua在一起就是正解了(大雾。
0x02 分析
题意不再赘述。
首先,这道题的答案一定是整个序列最大值的某个因数,对吧。
毕竟这个最大值一定会作为某个区间的最大值为答案做贡献。
那么我们尝试去枚举所有的因数 \(x\)。
直接定义 \(dp[i]\) 表示:对于 \(x\) 来说,我们最多能将原序列的前 \(i\) 位一点不剩地拆成 \(dp[i]\) 份,满足每一份的最大值都有一个因数为 \(x\)。
且对于一个区间 \([j, i]\) 以及原区间 \(a\),如果 \(x | \max \{a[j], a[j + 1] ... a[i] \}\)(也就是说它可以做合法的一份),则一定有 \(dp[i] = dp[j - 1] + 1\)。
那么对于我们枚举的 \(x\) ,\(dp[i] = \max \{dp[j - 1]\}(j \in \{k,x | \max \{a[k], a[k + 1] ... a[i] \}\})\)
显然对于每一个 \(x\),如果有 \(dp[n] \geq k\),则 \(x\) 可以作为答案,暴力枚举 \(x\) 再更新答案即可。
结了。涵妹给我讲的时候只用了不到 \(5\) 句话www。
由打表可发现,\(a[i]\) 最多有 \(240\) 个因数。所以最后极限时间复杂度为 \(O(240n^2)\),时限 \(2s\),可过。
0x03 具体实现
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
inline int Max(int x, int y) {return x > y ? x : y;}
int read() {
int x = 0, k = 1;
char s = getchar();
while(s < '0' || s > '9') {
if(s == '-')
k = -1;
s = getchar();
}
while(s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * k;
}
const int INF = 0x3f3f3f3f;
const int MAXN = 1e3 + 5;
int t[MAXN], len = 0, a[MAXN], dp[MAXN];
int main() {
int n = read(), k = read(), r = 0;
for(int i = 1; i <= n; i++) {
a[i] = read();
r = Max(r, a[i]);
}
for(int j = 1; j <= r; j++)
if(r % j == 0)
t[++len] = j;
int ans = 0;
for(int i = 1; i <= len; i++) {
memset (dp, -INF, sizeof dp);
dp[0] = 0;
for(int j = 1; j <= n; j++) {
int ma = -INF;
for(int k = j; k >= 1; k--) {
ma = Max(ma, a[k]);
if(ma % t[i] == 0)
dp[j] = Max(dp[j], dp[k - 1] + 1);
}
}
if(dp[n] >= k)
ans = t[i];
}
printf("%d\n", ans);
return 0;
}

浙公网安备 33010602011771号