CF650B 照片浏览
1 CF650B 照片浏览
2 题目描述
时间限制 \(1s\) | 空间限制 \(256M\)
\(Vasya\) 的手机里面有 \(n\) 张照片,编号为 \(1\) 的照片正在手机上显示。可以通过手指左右滑动来切换相邻的照片。如果你从第一张照片往左边滑动,你就到了第 \(n\) 张照片。类似的,如果你从第 \(n\) 张照片往右边滑动你就回到了第 \(1\) 张照片。滑动一张照片需要 \(a\) 秒钟。每张照片都有两种方向,横向或者纵向。我们可以直接观看纵向的照片。如果我们要观看横向的照片,我就需要花费 \(b\) 秒钟旋转成纵向。\(Vasya\) 有 \(T\) 秒钟的时间来查看照片,他想尽可能的多看照片。如果 \(Vasya\) 第一次打开照片,他会花 \(1\) 秒钟来查看照片的细节。如果照片的方向不对,在看照片之前他要先花费 \(b\) 秒钟来旋转照片。对于他曾经看过的照片,他会直接忽略,不花费任何时间在上面。不允许忽略没看过的照片。请求出 \(Vasya\) 在 \(T\) 秒内能看到最大的照片数量。
数据范围:\(1 ≤ n ≤ 5·105, 1 ≤ a, b ≤ 1000, 1 ≤ T ≤ 109\)
3 题解
我们发现,由于我们必须看完当前照片再往后翻,我们看到第 \(i\) 张照片所需要的时间其实是确定的。这里我们分三部分计算:往左翻、往右翻、以及先看完一侧后翻到另一侧的时间。如果某张照片在一开始是横着的,那么我们看这张照片的时间就是 \(b + 1\),否则我们看这张照片的时间就是 \(1\)。这里我们设 \(t_i\) 为看第 \(i\) 张照片所用的时间,用 \(sum_1\) 存储向右翻时的时间,用 \(sum_2\) 存储向左翻时的时间。那么就有 \(sum_{1, i} = sum_{1, i-1} + t_i + a + 1\), \(sum_{2, i} = sum_{1, i-1} + t_{n - i + 1} + a + 1\)。这里的 \(sum_{1, i}\) 表示从第一张向右翻 \(i\) 张所需要的时间,\(sum_{1, i}\) 表示从第一张向右翻 \(i\) 张所需要的时间,\(sum_{2, i}\) 表示从第一张向左翻 \(i\) 张所需要的时间。我们可以考虑枚举向左翻动的张数 \(i\),然后二分找到耗费时间最大的我们可以向右翻动看到的照片数 \(j\)。注意这里我们默认是先向左翻,所以总共花费的时间就是 \(sum_{1,i} + i * a + sum_{2, j}\)。然后我们反着枚举向右翻动的张数 \(j\),然后二分找到耗费时间最大的我们可以看到向左翻动看到的照片数 \(i\)。由于默认先向右翻,所以总共花费的时间是 \(sum_{2,j} + j * a + sum_{1, i}\)。将这两种分配方案能看到的照片数取一个 \(max\),再与 \(n\) 取一个 \(min\) 输出。这里与 \(n\) 取 \(min\) 是因为我们枚举的向左和向右的照片可能会重叠,导致答案偏大。
注意在开头减去看第 \(1\) 张照片的时间。
4 代码(空格警告):
#include <iostream>
#include <string>
using namespace std;
const int N = 5e5+10;
int n, a, b, T, l, r, mid, ans, fans;
int sum1[N], sum2[N];
char s[N];
int main()
{
cin >> n >> a >> b >> T >> (s+1);
if (s[1] == 'w') T -= b;
T -= 1;
if (T < 0)
{
cout << 0;
return 0;
}
for (int i = 1; i < n; i++) sum1[i] = sum1[i-1] + (s[i+1] == 'w') * b + a + 1;
for (int i = 1; i < n; i++) sum2[i] = sum2[i-1] + (s[n-i+1] == 'w') * b + a + 1;
for (int i = 0; i < n; i++)
{
l = 0;
r = n-1;
while (l <= r)
{
mid = (l+r)/2;
if (T >= sum1[i] + i * a + sum2[mid]) ans = mid, l = mid+1;
else r = mid-1;
}
if (sum1[i] + i * a + sum2[ans] > T) continue;
fans = max(fans, ans + i + 1);
}
for (int i = 0; i < n; i++)
{
l = 0;
r = n-1;
while (l <= r)
{
mid = (l+r)/2;
if (T >= sum2[i] + i * a + sum1[mid]) ans = mid, l = mid+1;
else r = mid-1;
}
if (sum2[i] + i * a + sum1[ans] > T) continue;
fans = max(fans, ans + i + 1);
}
cout << min(n, fans);
return 0;
}