序列(array)
题目描述
给定一个长为
m
m
m 的序列
a
a
a。
有一个长为
m
m
m 的序列
b
b
b,需满足
0
≤
b
i
≤
n
0\leq b_i \leq n
0≤bi≤n,
∑
i
=
1
m
a
i
b
i
≤
D
\sum_{i=1}^m a_ib_i \leq D
∑i=1maibi≤D 且
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,m≤100。
对于
30
%
30\%
30% 的数据,
n
≤
1
0
6
n \leq 10^6
n≤106,
m
≤
100
m \leq 100
m≤100。
对于另
30
%
30\%
30% 的数据,
T
=
1
T = 1
T=1 且数据随机。
对于
100
%
100\%
100% 的数据,
1
≤
n
≤
1
0
9
1 \leq n \leq 10^9
1≤n≤109,
1
≤
k
,
m
≤
2
×
1
0
5
1\leq k, m \leq 2\times 10^5
1≤k,m≤2×105,
1
≤
D
≤
1
0
18
1 \leq D \leq 10^{18}
1≤D≤1018,
1
≤
a
i
≤
5000
1\leq a_i \leq 5000
1≤ai≤5000。
时空限制
时间限制:2s
空间限制:256MB
题解
首先贪心策略,若
a
i
≤
a
j
a_i \le a_j
ai≤aj,则
b
i
≥
b
j
b_i \ge b_j
bi≥bj。把
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
s∗n+x+⌊∑i=s+2naiD−n∑i=1sai−as+1∗x⌋
去掉
s
∗
n
s*n
s∗n,是形如
x
+
a
⌊
b
x
+
c
d
⌋
x+a\lfloor\dfrac{bx+c}{d}\rfloor
x+a⌊dbx+c⌋的,这个函数形如锯齿
三种情况会达到最大值
-
第一个峰
-
最后一个峰
-
结束点
对应到题目上就是
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;
}