20210819 1554.洛谷月赛

19 篇文章 0 订阅
本文探讨了一种使用分治策略解决将序列分割成若干段的问题,其中每段的最大值都是该段中某个数的约数。通过找到序列中的最大值并检查其约数是否满足条件,来确定分割的可能性。代码实现中包含了暴力枚举最大值的约数和递归分治函数。此外,还提到可以通过RMQ(Range Minimum Query)优化查询最大值的过程,但根据数据规模,原代码已经足够应对。
摘要由CSDN通过智能技术生成

请添加图片描述
请添加图片描述

题解:

首先:我们可以推出答案一定是最大数的约数

然后,我们就可以先暴力枚举出最大值的约数

再,判断其是否可行

其方法是: 分治

find ( l , r , num ) 为在 l ~ r 这段区间之内最多分成几段

那就先找 1 ~ n 这段区间的最大值,然后看其是否可以整除 num

若可,再在其左边及右边找一下即可

若不可,则也就是说要将其与另一个比它更大的数合并

当然这个过程有很多小细节,如:( 在不可整除的情况下 )

  1. 当 l 为 1 时,则这个数必须与右边合并
  2. 当 r 为 n 时,则这个数必须与左边合并

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#define LL long long
#define ull unsigned LL
#define db double
#define ldb long db
#define INF 1000000009
using namespace std;

const int N=1e6+100,M=1010;
int n,m,wz,maxn,k,a[N],s[N];
int find ( int l,int r )
{
	int wz=0,maxn=0;
	for ( int i=l;i<=r;i++ ) if ( a[i]>maxn ) maxn=a[i],wz=i;
	return wz; 
}
int pd ( int l,int r,int num )
{
	if ( l>r ) return 0;
	int wz=find ( l,r );
	if ( a[wz]%num==0 ) return pd ( l,wz-1,num ) + pd ( wz+1,r,num ) + 1;
	if ( l==1 ) return pd ( l,wz-1,num );
	if ( r==n ) return pd ( wz+1,r,num );
	return max ( pd ( l,wz-1,num ),pd ( wz+1,r,num ) );
}

int main ( )
{
	scanf ( "%d %d",&n,&k );
	for ( int i=1;i<=n;i++ ) scanf ( "%d",&a[i] ),maxn=max ( maxn,a[i] );
	for ( int i=1;i<=maxn/i;i++ )
	{
		if ( i*i==maxn ) s[++m]=i;
		else if ( maxn%i==0 ) s[++m]=i,s[++m]=maxn/i;
	}
	sort ( s+1,s+1+m );
	for ( int i=m;i>=1;i-- ) if ( pd ( 1,n,s[i] )>=k ) { printf ( "%d",s[i] );break; }//printf ( "    %d\n",pd ( 1,n,s[i] ) );
	return 0;
}

最后其实还有一个优化:

查询 l ~ r 的最大值可以用RMQ

但我看了看数据还可以撑过去,就咳咳了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值