HDU 3486 题解

博客讲述了如何解决HDU 3486问题,通过二分查找和RMQ(Range Minimum Query)技术,在分组元素中寻找最大值之和大于k的方案。内容强调了当组数增加时,最大值之和可能增大,以及暴力枚举的低效,并详细解释了利用单调性和ST表构建f数组的方法来优化求解过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HDU 3486

总共n个元素,分成m段,每一段挑选出该段中的最大值,将最大值求和,看是否严格大于k。如果找不到输出-1。

如果分的组比较少,每一组元素比较多,那么所得的和就会比较小;如果组分得多,原来比较小的就有可能会成为最大的,那么所得的和也会大。如果每一个数,即每一个数一组,他们的和都不大于k的话,说明根本找不到m,输出-1

可能大家会想到暴力,用k除以最大的元素找到m的最小值,然后m不断++,暴力枚举。但这样实在是太慢了!为什么呢?首先分个组数,m不断加一,k可以到10个亿啊,组数会很多。。。

解法:二分+RMQ

如果分成三组可以,那么分成五组,六组,七组......肯定会更好,这就是明显的单调性!可以用二分!

至于RMQ就是求区间最值,用f数组构建ST表。f[i][j]表示以a[i]开头,长度为2^j的连续区间的最值。

//HDU 3486
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=200000+5;

int n,sum;
ll k;
int f[MAXN][25];

void RMQ()
{
	for(int j=1;j<=log2(n);j++) 
	  for(int i=0;i+(1<<j)-1<n;i++)
	    f[i][j]=max(f[i][j-1],f[i+2<<(j-1)][j-1]); 
}

int search(int L, int R)//查询函数 
{
    int k=0;
    
	while(1<<(k+1)<=R-L+1) k++;
    
	return max(f[L][k],f[R-(1<<k)+1][k]);
}
bool check(int len,int s)//len 每一组的长度,s是组数 
{
	int sum=0;
    
    for(int i=1; i<=s; i++)
	{
        int l=(i-1)*len; 
        int r=len*i-1;
        sum+=search(l,r);//查表 
        if(sum>k) 
            return true;
    }
    return false;
}
int main()
{
	while(cin>>n>>k)//while(~scanf("%d%d",&n,&k))
	{
		if(n==-1&&k==-1) break;
		
		for(int i=0;i<n;i++)
		{
			cin>>f[i][0];
			sum+=f[i][0];
		}
		if(sum<=k)
		{
			cout<<"-1";
			continue;
		}
		RMQ();
		int ans,l=1,r=n;//l是指二分分了多少组,最起码也得1组,最多n组 
        while(l<=r)
		{
            int mid=(l+r)/2;
            if(check(n/mid,mid))//分了mid组,n/mid是每一组的大小 
			{
                ans=mid;//如果返回真,说明组数多了,再看能不能在小点 
                r=mid-1;
            }
            else
                l=mid+1;
        }
        
        cout<<ans;
	}
	
	
  return 0;
}
 

写作不易,麻烦动手点个赞,懂?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值