题目链接
解题思路
注意到原题中有“至多花费B元可得到的最多稻米数”,故这题可以用二分答案法做。
但我们注意到这题二分稻米数并不好做。
这里介绍一种two-points的方法。我们注意到
x
i
x_i
xi是递增的。在递增的序列中维护一个区间可以采用two-points法.
用
l
l
l指向区间的左端点,而
r
r
r指向区间的右端点。我们可以发现若区间
[
l
,
r
+
1
]
[l,r+1]
[l,r+1]满足花费
≤
B
\leq B
≤B,则区间
[
l
,
r
]
[l,r]
[l,r]的花费也会
≤
B
\leq B
≤B。我们只要向右移动右指针
r
r
r,直到
[
l
,
r
+
1
]
[l,r+1]
[l,r+1]无法满足条件时,记录下
[
l
,
r
]
[l,r]
[l,r]的值。接着,移动左端点
l
l
l,判断新的区间
[
l
+
1
,
r
]
[l+1,r]
[l+1,r]是否满足条件,然后再移动
r
r
r……这样的时间复杂度为
O
(
n
)
O(n)
O(n)。有兴趣的可以去搜索two-points。
接着就是如何算出区间
[
l
,
r
]
[l,r]
[l,r]内的米仓的花费。我们只要判断运载这个区间内的稻米到达米仓的花费
w
w
w的最小值是否
≤
B
\leq B
≤B即可。
w
=
∑
i
=
l
r
∣
x
[
i
]
−
k
∣
w=\sum_{i=l}^r|x[i]-k|
w=i=l∑r∣x[i]−k∣
k
k
k为选定的米仓的坐标值。易得当
k
k
k等于
x
[
l
+
r
2
]
x[\frac{l+r}{2}]
x[2l+r]时,
w
w
w最小。
问题就是怎么计算
w
m
i
n
=
∑
i
=
l
r
∣
x
[
i
]
−
x
[
l
+
r
2
]
∣
w_{min}=\sum_{i=l}^r|x[i]-x[\frac{l+r}{2}]|
wmin=∑i=lr∣x[i]−x[2l+r]∣了。
设
m
=
⌊
l
+
r
2
⌋
m= \left \lfloor \frac{l+r}{2} \right \rfloor
m=⌊2l+r⌋
w
m
i
n
=
∑
i
=
l
r
∣
x
[
i
]
−
x
[
m
]
∣
w_{min}=\sum_{i=l}^r|x[i]-x[m]|
wmin=i=l∑r∣x[i]−x[m]∣
=
∑
i
=
m
+
1
r
(
x
[
i
]
−
x
[
m
]
)
+
∑
j
=
l
m
−
1
(
x
[
m
]
−
x
[
j
]
)
=\sum_{i=m+1}^r(x[i]-x[m])+\sum_{j=l}^{m-1}(x[m]-x[j])
=i=m+1∑r(x[i]−x[m])+j=l∑m−1(x[m]−x[j])
=
∑
i
=
m
+
1
r
x
[
i
]
−
(
r
−
m
)
∗
x
[
m
]
+
(
m
−
l
)
∗
x
[
m
]
−
∑
j
=
l
m
−
1
x
[
j
]
=\sum_{i=m+1}^rx[i]-(r-m)*x[m]+(m-l)*x[m]-\sum_{j=l}^{m-1}x[j]
=i=m+1∑rx[i]−(r−m)∗x[m]+(m−l)∗x[m]−j=l∑m−1x[j]
设
s
u
m
[
i
]
表
示
x
[
i
]
的
前
缀
和
sum[i]表示x[i]的前缀和
sum[i]表示x[i]的前缀和
则
原
式
=
(
s
u
m
[
r
]
−
s
u
m
[
m
]
)
−
(
r
−
m
)
∗
x
[
m
]
原式=(sum[r]-sum[m])-(r-m)*x[m]
原式=(sum[r]−sum[m])−(r−m)∗x[m]
+
(
m
−
l
)
∗
x
[
m
]
−
(
s
u
m
[
m
−
1
]
−
s
u
m
[
l
−
1
]
)
+(m-l)*x[m]-(sum[m-1] -sum[l-1])
+(m−l)∗x[m]−(sum[m−1]−sum[l−1])
O
(
1
)
O(1)
O(1)内算出
w
m
i
n
w_{min}
wmin,nice!
详细代码
#define rg register
#define il inline
#define DEBUG printf("[Passing [%s] in line %d.]\n", __func__, __LINE__)
#define putline putchar('\n')
#define putsp putchar(' ')
#define Rep(a, s, t) for(rg int a = s; a <= t; a++)
#define Repdown(a, t, s) for(rg int a = t; a >= s; a--)
typedef long long ll;
#include<cstdio>
#define rs freopen("test.in", "r", stdin), freopen("test.out", "w", stdout)
struct IO
{
IO(int set = 0) {if(set) rs;}
template<typename T> il IO r(T& x)const
{
x = 0; T f = 1; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
x *= f; return *this;
}
template<typename T> il IO w(T x)const
{
if(x < 0) {putchar('-'); x = -x;}
if(x >= 10) w(x / 10);
putchar(x % 10 + '0'); return *this;
}
template<typename T> il IO wl(const T& x)const {w(x), putline; return *this;}
template<typename T> il IO ws(const T& x)const {w(x), putsp; return *this;}
il IO l() {putline; return *this;}
il IO s() {putline; return *this;}
}io;
template<typename T> il T Max(const T& x, const T& y) {return y < x ? x : y;}
template<typename T> il T Min(const T& x, const T& y) {return y < x ? y : x;}
template<typename T> il void Swap(T& x, T& y) {T tmp = x; x = y; y = tmp;}
int N, L;
ll B;
ll x[100005];
ll sum[100005];
int ans;
bool check(int l, int r)
{
int mid = (l + r) >> 1;
ll sum1 = sum[r] - sum[mid] - (r - mid) * x[mid];
ll sum2 = (mid - l) * x[mid] - (sum[mid - 1] - sum[l - 1]);
return sum1 + sum2 <= B;
}
int main()
{
//FileReset();
io.r(N).r(L).r(B);
for(rg int i = 1; i <= N; i++) io.r(x[i]), sum[i] = sum[i - 1] + x[i];
int r = 1;
for(rg int l = 1; l < N; l++)
{
while(r < N && check(l, r + 1)) r++;
ans = Max(ans, r - l + 1);
}
io.wl(ans);
return 0;