最大公约数

寻求最大公约数是人民民主的真谛。

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;
}
posted @ 2020-11-28 16:39  STrAduts  阅读(57)  评论(1)    收藏  举报