【题解】最大公约数

题面

题目背景

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

题目描述

小 L 极其喜爱 \(\gcd\)

有一天,小 Z :小 L,问你个题。

小 Z:给出一个长度为 \(n\) 的序列 \(a\) 和一个整数 \(k\) ,把这个序列划分成连续的 \(k\) 段,每一段不能为空。

小 L:等等,\(\gcd\) 去哪里了。

小 Z:别急嘛,我们设 \(b_i\) 表示第 \(i\) 段的最大值,请问怎么划分该序列使得 \(\gcd(b_1,b_2,...,b_k)\) 的值最大。

小 L 沉思了一会,发现自己不会做,彻底地自闭了,于是他又向你来求助了,请你帮帮他。

输入格式

输入文件名为 gcd.in

输入文件第一行包含两个整数\(n\) , \(k\),表示序列的长度和需要划分的段数。

接下来第二行包含 \(n\) 个整数,表示这个序列。

输出格式

输出文件名为 gcd.out

输出文件包含一个整数,表示你的答案。

样例

样例输入 1

5 3
1 3 2 9 6

样例输出 1

3

样例解释 1

最优的划分可能有多种,这里给出一种最优的划分,将序列分成 \([1,3]\) , \([2,9]\) , \([6]\) 三段,其中 \(b_1=3\) ,\(b_2=9\) , \(b_3=6\),所以答案为 \(\gcd(3,9,6)=3\)

数据规模与约定

对于 \(100\%\) 的数据,\(1\leqslant k\leqslant n\leqslant 10^3\) , \(1\leqslant k \leqslant 5\times 10^2\) , \(1\leqslant a_i\leqslant 10^6\)

Solution

出题人米巴米巴吃多了,一上来就给一道数论?

彻底自闭的是我才对吧。。。【欲哭无泪.gif】

看起来。。。貌似是道。。。DP?[凭感觉做题晚期患者再次上线]


每个分段的最大值里面,一定会有整个序列的最大值,也就是说,最终求到的 \(\gcd\) ,一定是最大值的因数。

因为分组未知的情况下,只能求得整个序列的最大值,我们以此为突破口。

设最大值为 \(mx\) .

枚举 \(mx\) 的每个因数为最大公约数的情况, \(divi_i\) 表示 \(mx\)\(1\) 开始的第 \(i\) 个因数。

那么,我们已经(其实是我)想象到了这可能是一道DP,那么状态呢?

对于 divi[i] , dp[j] 表示前 j 个数可以分成的最大段数,并且每一段的最大值都是 divi[i] 的倍数

状态转移方程的柿子呢?

设 mxx 为 a[k]~a[j](k<=j) 的最大值
如果 divi[i] 整除 mxx:
	dp[j]=max(dp[j],dp[k-1]+1);

为什么?

既然 \(mxx\) 是当前的最大值,并且 \(mxx\) 也是 \(divi_i\) 的倍数,那么 \(mxx\) 就可以作为当前分段的最大值。

而我们要保证可以分的段数最大,我们想,\(mxx\) 表示的是 \(a_k\) ~ \(a_j\) 的最大值,那么,我们就将 \(a_k\) ~ \(a_j\) 分为一段不就行了?

而分到这里最大的段数就是分到 \(a_{k-1}\) 时的最大段数加上当前段的 \(1\)


如果对于 \(divi[i]\)\(dp_n\) 可以分的段数 \(\geqslant k\),因为 \(divi_i\) 是从小到大排列的,最新的一定是最大的,我们直接更新答案。


初始化时,因为要求最大值,初始化成 \(-0x3f3f3f3f\)\(dp_0=0\)


Code

#include<cstdio>
#include<cstring>
const int maxn=1e3+5;
int a[maxn];
int dp[maxn];
int divi[maxn];
int n,k,mx,tot,ans;
int max(int x,int y){
	return x>y?x:y;
}
void read(int&x){
	x=0;
	bool f=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		x=x*10+(ch&15);
		ch=getchar();
	}
	if(f)x=-x;
	return;
}
int main(){
	read(n);read(k);
	for(int i=1;i<=n;++i){
		read(a[i]);
		mx=max(mx,a[i]);
	}
	for(int i=1;i<=mx;++i){
		if(mx%i==0)
			divi[++tot]=i;
	}
	for(int i=1;i<=tot;++i){
		memset(dp,-0x3f,sizeof(dp));
		dp[0]=0;
		for(int j=1;j<=n;++j){
			int mxx=-0x3f3f3f3f;
			for(int k=j;k;--k){
				mxx=max(mxx,a[k]);
				if(mxx%divi[i]==0)
					dp[j]=max(dp[j],dp[k-1]+1);
			}
		}
		if(dp[n]>=k)ans=divi[i];
	}
	printf("%d",ans);
	return 0;
}

end.

posted @ 2020-11-28 21:53  XSC062  阅读(61)  评论(0编辑  收藏  举报