【题解】最大公约数
题面
题目背景
寻求最大公约数是人民民主的真谛。
题目描述
小 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.
—— · EOF · ——
真的什么也不剩啦 😖