Luogu P7244 章节划分

这题从一月初看题解做到现在,必须写个题解纪念下。

Link

下文中 \(\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\) 最多能分成多少个合法区间,那么转移方程为:

\[f(i)=\max\limits_{j=1}^{i-1}\left\{f(j)+[\max\limits_{k=j+1}^i\left\{a_k\right\}\bmod x=0]\right\} \]

上面这种转移的时间复杂度为 \(\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

上一篇:异常值检测


下一篇:ITK:分段血管