这题从一月初看题解做到现在,必须写个题解纪念下。
下文中 (sigma_k(x)) 表示 (x) 所有因数的 (k) 次方和。根据定义,(sigma_0(x)) 表示 (x) 所有因数的 (0) 次方和即 (x) 的因数数量。
由于答案一定是 (max{a_i}) 的因数,所以 (mathcal O(sigma_0(max{a_i}))) 枚举答案 (x),转化为判定问题。定义一个区间 ([l,r]) 是合法的,当且仅当 ([l,r]) 的最大值 (y) 满足 (ymid x)。注意到两个合法的区间合并后仍然是合法的区间,所以可以设计一个 dp:令 (f(i)) 表示 (1sim n) 最多能分成多少个合法区间,那么转移方程为:
[f(i)=maxlimits_{j=1}^{i-1}left{f(j)+[maxlimits_{k=j+1}^ileft{a_k
ight}mod x=0]
ight}
]
上面这种转移的时间复杂度为 (mathcal O(n^2sigma_0(max{a_i}))) 的,考虑优化。令 (a_0=0),(p) 为满足 (a_p>a_i;(0le p<i)) 的最大的 (p),那么 ([1,p)) 的所有转移一定劣于 ([p,i-1]) 的转移,在 ([p,i-1]) 再分一段是更优的,而这个区间的 (max) 一定都是 (a_i),所以转移时只考虑 (f(p)sim f(i-1)) 即可,通过线段树维护可以实现 (mathcal O(nlog nsigma_0(max{a_i}))) 的时间复杂度。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5;
int n,k,a[N+10];
void Max(int &x,int y) {x=max(x,y);}
//segment tree
int m[N*4+10];
void modify(int p,int l,int r,int ml,int d)
{
// printf("modify(%d, %d, %d)
",p,l,r);
if(l==r)
{
m[p]=d;
return;
}
int mid=(l+r)/2;
if(ml<=mid) modify(p*2,l,mid,ml,d);
else modify(p*2+1,mid+1,r,ml,d);
m[p]=max(m[p*2],m[p*2+1]);
}
int query(int p,int l,int r,int ql,int qr)
{
if(ql>qr) return -0x3f3f3f3f;
if(ql<=l && r<=qr) return m[p];
int mid=(l+r)/2,ans=-0x3f3f3f3f;
if(ql<=mid) Max(ans,query(p*2,l,mid,ql,qr));
if(qr>mid) Max(ans,query(p*2+1,mid+1,r,ql,qr));
return ans;
}
// stack
int st[N+10],top=0,pre[N+10];
void getpre()
{
st[++top]=n;
for(int i=n-1;i;i--)
{
while(top && a[st[top]]<a[i]) pre[st[top--]]=i;
st[++top]=i;
}
// while(top) pre[st[top--]]=0;
}
// dp
int dp[N+10];
bool check(int x)
{
// printf("x=%d
",x);
memset(dp,-0x3f,sizeof(dp));
dp[0]=0;
memset(m,-0x3f,sizeof(m));
for(int i=1;i<=n;i++)
{
if(pre[i]) Max(dp[i],dp[pre[i]]);
if(a[i]%x==0)
{
if(!pre[i]) Max(dp[i],1);
Max(dp[i],query(1,1,n,max(1,pre[i]),i-1)+1);
// printf("i=%d, query(%d, %d):%d
",i,max(1,pre[i]),
// i-1,query(1,1,n,max(1,pre[i]),i-1));
}
// printf("dp[%d]:%d
",i,dp[i]);
modify(1,1,n,i,dp[i]);
}
// for(int i=1;i<=n;i++) printf("%d ",dp[i]);
// putchar('
');
return dp[n]>=k;
}
int main()
{
scanf("%d %d",&n,&k);
int mx=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
Max(mx,a[i]);
}
getpre();
// for(int i=1;i<=n;i++) printf("%d ",pre[i]);
// putchar('
');
for(int i=mx;i;i--)
{
if(mx%i) continue;
if(check(i))
{
printf("%d",i);
return 0;
}
}
return 0;
}
感谢:
https://www.cnblogs.com/zzctommy/p/14226568.html
https://www.luogu.com.cn/blog/cqbzljs/glr-round-2-luo-shui-zhi-ling-round-2-zhang-jie-hua-fen-ti-xie