【2020.11.28提高组模拟】T2 序列(array)

序列(array)

题目描述

​给定一个长为 m m m 的序列 a a a
有一个长为 m m m 的序列 b b b,需满足 0 ≤ b i ≤ n 0\leq b_i \leq n 0bin ∑ i = 1 m a i b i ≤ D \sum_{i=1}^m a_ib_i \leq D i=1maibiD b i b_i bi 为整数。
​求 ∑ b i + k min ⁡ i = 1 m b i \sum b_i + k \min_{i=1}^m{b_i} bi+kmini=1mbi 的最大值。

输入格式

​第一行一个正整数 T T T,表示数据组数。
​对于每组数据,第 1 1 1 行四个整数 n , m , k , D n, m, k, D n,m,k,D
​第 2 2 2 m m m 个整数 a i a_i ai

输出格式

​对于每组数据,第一行一个整数 a n s ans ans

数据范围

​对于 15 % 15\% 15% 的数据, n , m ≤ 100 n, m \leq 100 n,m100
​对于 30 % 30\% 30% 的数据, n ≤ 1 0 6 n \leq 10^6 n106 m ≤ 100 m \leq 100 m100
​对于另 30 % 30\% 30% 的数据, T = 1 T = 1 T=1 且数据随机。
​对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1n109 1 ≤ k , m ≤ 2 × 1 0 5 1\leq k, m \leq 2\times 10^5 1k,m2×105 1 ≤ D ≤ 1 0 18 1 \leq D \leq 10^{18} 1D1018 1 ≤ a i ≤ 5000 1\leq a_i \leq 5000 1ai5000

时空限制

​时间限制:2s
​空间限制:256MB

题解

首先贪心策略,若 a i ≤ a j a_i \le a_j aiaj,则 b i ≥ b j b_i \ge b_j bibj。把 a a a排序,枚举 b b b中有多少个是 n n n。若到 n n n的有 s s s,设 b s + 1 = x b_{s+1}=x bs+1=x,则答案为 s ∗ n + x + ⌊ D − n ∑ i = 1 s a i − a s + 1 ∗ x ∑ i = s + 2 n a i ⌋ s*n+x+\lfloor\dfrac{D-n\sum^s_{i=1}a_i-a_{s+1}*x}{\sum^n_{i=s+2}a_i}\rfloor sn+x+i=s+2naiDni=1saias+1x
去掉 s ∗ n s*n sn,是形如 x + a ⌊ b x + c d ⌋ x+a\lfloor\dfrac{bx+c}{d}\rfloor x+adbx+c的,这个函数形如锯齿
三种情况会达到最大值

  1. 第一个峰
    在这里插入图片描述

  2. 最后一个峰
    在这里插入图片描述

  3. 结束点
    在这里插入图片描述

对应到题目上就是
1.尽可能提高 m i n b minb minb,剩余去提高 b s b_s bs
2.尽可能提高 b s b_s bs,剩余去提高 m i n b minb minb
3. m i n b + 1 minb+1 minb+1,剩下全部提高 b s b_s bs

Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll t,n,m,k,d,ans,x,ans1,ans2,ans3,minb,a[200001],sum[200001];
ll calc(int i,int minb) {return n*(i-1)+min((x-minb*(sum[m]-sum[i]))/a[i],n)+minb*(k+m-i); }
int main()
{
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	scanf("%lld",&t);
	while (t--)
	{
		scanf("%lld%lld%lld%lld",&n,&m,&k,&d);
		ans=0;
		for (int i=1;i<=m;++i)
			scanf("%lld",&a[i]);
		sort(a+1,a+m+1);
		for (int i=1;i<=m;++i)
			sum[i]=sum[i-1]+a[i];
		for (int i=1;i<=m;++i) //a[1]~a[i-1]=n
		{
			x=d-n*sum[i-1];
			if (x<0) break;
			minb=(x-a[i]*(n+1))/(sum[m]-sum[i])+1;//a[i]~a[n]>=minb
			ans1=x/(sum[m]-sum[i-1]);//方案1,使b[s]和b[s-1]~b[m]一样
			if (ans1<0) ans1=0;
			if (ans1>n) ans1=n;
			ans=max(ans,calc(i,ans1));
			ans2=min(ans1,(x-min((x-minb*(sum[m]-sum[i]))/a[i],n))/(sum[m]-sum[i]));//b[s]取最大
			if (ans2<0) ans2=0;
			if (ans2>n) ans2=n;
			ans=max(ans,calc(i,ans2));
			ans3=min(ans1,minb);//minb在前面已经先加了1
			if (ans3<0) ans3=0;
			if (ans3>n) ans3=n;
			ans=max(ans,calc(i,ans3));
			//ans1,ans2,ans3都代表着b[s-1]~b[m]
		}
		printf("%lld\n",ans);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值