【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.尽可能提高\(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;
}