Luogu P7244 章节划分
这题从一月初看题解做到现在,必须写个题解纪念下。
下文中 \(\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\) 满足 \(y\mid x\)。注意到两个合法的区间合并后仍然是合法的区间,所以可以设计一个 dp:令 \(f(i)\) 表示 \(1\sim n\) 最多能分成多少个合法区间,那么转移方程为:
上面这种转移的时间复杂度为 \(\mathcal O(n^2\sigma_0(\max\{a_i\}))\) 的,考虑优化。令 \(a_0=0\),\(p\) 为满足 \(a_p>a_i\;(0\le p<i)\) 的最大的 \(p\),那么 \([1,p)\) 的所有转移一定劣于 \([p,i-1]\) 的转移,在 \([p,i-1]\) 再分一段是更优的,而这个区间的 \(\max\) 一定都是 \(a_i\),所以转移时只考虑 \(f(p)\sim f(i-1)\) 即可,通过线段树维护可以实现 \(\mathcal O(n\log n\sigma_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)\n",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\n",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\n",i,max(1,pre[i]),
// i-1,query(1,1,n,max(1,pre[i]),i-1));
}
// printf("dp[%d]:%d\n",i,dp[i]);
modify(1,1,n,i,dp[i]);
}
// for(int i=1;i<=n;i++) printf("%d ",dp[i]);
// putchar('\n');
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('\n');
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