Loading

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

序列(array)

题目描述

​给定一个长为 \(m\) 的序列 \(a\)
有一个长为 \(m\) 的序列 \(b\),需满足 \(0\leq b_i \leq n\)\(\sum_{i=1}^m a_ib_i \leq D\)\(b_i\) 为整数。
​求 \(\sum b_i + k \min_{i=1}^m{b_i}\) 的最大值。

输入格式

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

输出格式

​对于每组数据,第一行一个整数 \(ans\)

数据范围

​对于 \(15\%\) 的数据,\(n, m \leq 100\)
​对于 \(30\%\) 的数据,\(n \leq 10^6\)\(m \leq 100\)
​对于另 \(30\%\) 的数据,\(T = 1\) 且数据随机。
​对于 \(100\%\) 的数据,\(1 \leq n \leq 10^9\)\(1\leq k, m \leq 2\times 10^5\)\(1 \leq D \leq 10^{18}\)\(1\leq a_i \leq 5000\)

时空限制

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

题解

首先贪心策略,若\(a_i \le a_j\),则\(b_i \ge b_j\)。把\(a\)排序,枚举\(b\)中有多少个是\(n\)。若到\(n\)的有\(s\),设\(b_{s+1}=x\),则答案为$$s*n+x+\lfloor\dfrac{D-n\sums_{i=1}a_i-a_{s+1}*x}{\sumn_{i=s+2}a_i}\rfloor$$
去掉\(s*n\),是形如\(x+a\lfloor\dfrac{bx+c}{d}\rfloor\)的,这个函数形如锯齿
三种情况会达到最大值

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

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

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

对应到题目上就是
1.尽可能提高\(minb\),剩余去提高\(b_s\)
2.尽可能提高\(b_s\),剩余去提高\(minb\)
3.\(minb+1\),剩下全部提高\(b_s\)

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;	
}
posted @ 2020-11-29 19:56  Thunder_S  阅读(202)  评论(0编辑  收藏  举报