HDU6444:数论+最大字段和

本文针对HDU6444问题提供了解决方案,通过分析环的特性和使用单调队列来求解最大连续字段和。讨论了如何根据环的权值之和采取不同的策略,并给出了具体的实现代码。

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

HDU6444

题解

  • 环的大小为n,每次跳k步,所有有gcd(n,k)个不同的环,每个环的大小为len = n / gcd(n,k)。接下来对每一个环进行分析。
  • 先对环的权值求和sum
  • 如果sum ≤0,那么找长度不大于len的最大连续字段和。
  • 如果sum >0num = m / len表示完整的圈的个数,b = m % len表示剩下的步数。有两种方案。
  • 一种是先跑完num个圈,再找长度不大于b的最大连续字段和。
  • 另外一种是先跑完num-1个圈,再找长度不大于len的最大连续字段和。
  • 取上面两种的最大值。
  • 求最大连续字段和用单调队列求解。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll const inf = 0x7f7f7f7f7f7f;
int const N = 100000 + 10;
int n,m,k,len,deq[N];
ll s,a[N],sum[N<<1],b[N<<1];
ll getsum(int n,int m){   //求长度不超过m的最大连续字段和
	int s = 1,t = 1;
	ll ans = -inf;
	deq[t++] = 0;
	for(int i=1;i<=n;i++){
		while(s < t && sum[deq[t-1]] > sum[i])	t--;
		deq[t++] = i;
		ans = max(ans,sum[i] - sum[deq[s]]);
		if(i - deq[s] == m)		s++;
	}
	return ans;
}
ll solve(int s){ 
	int cnt = 0,p = s;  
	while(cnt != len){
		b[++cnt] = a[p];
		p = (p + k) % n;
	}
	for(int i=1;i<=cnt;i++)	b[i+cnt] = b[i];
	for(int i=1;i<=2*cnt;i++)	sum[i] = sum[i-1] + b[i];
	if(sum[cnt] <= 0)	return getsum(cnt<<1,len);    //求长度不超过m的最大连续字段和
	else{
		int num = m / len,	b = m % len;  //num表示完整圈的个数,b表示剩下的一段
		if(num > 0){
			ll res1 = num * sum[cnt] + getsum(cnt<<1,b);
			ll res2 = (num - 1) * sum[cnt] + getsum(cnt<<1,len);
			return max(res1,res2);
		}else	return getsum(cnt<<1,b);
	}
}
int main(){
	int T,caser = 0;
	scanf("%d",&T);
	while(T--){
		scanf("%d%lld%d%d",&n,&s,&m,&k);   //圈的大小为n,每次跳k个,最多可以取m次,要求价值总和至少为s
		for(int i=0;i<n;i++)	scanf("%lld",&a[i]);
		int num = __gcd(n,k);   //圈的个数
		len = n / num;   //len表示每个圈的长度
		ll res = -inf;
		for(int i=0;i<num;i++)	res = max(res,solve(i));
		printf("Case #%d: %lld\n",++caser,res >= s ? 0 : s - res);
	}
	return 0;
}	

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值