Problem - 1791G2 - Codeforces
这道题给定一个数轴上的点 0,1,...,n+1,其中每个点 i (1 ≤ i ≤ n) 都有一个传送门。在第 i 个点,你可以进行以下操作:
向左移动一格:花费 1 个金币。
向右移动一格:花费 1 个金币。
使用该位置 i 上的传送门(如果存在):花费 ai 个金币。使用传送门后,你可以选择传送到点 0 或点 n+1。一旦使用了某个传送门,就无法再次使用它。
你有 c 个金币,一开始位于点 0。问你最多可以使用多少个传送门。
输入格式: 第一行一个整数 t(1≤t≤1000),表示测试用例个数。 接下来 t 行,每行两个整数 n、c(1≤n≤2⋅105;1≤c≤109),表示数组长度和可用金币数。 接下来一行 n 个整数 a1,a2,…,an(1≤ai≤109),表示传送门使用所需金币数。
输出格式: 对于每个测试用例,输出你最多可以使用的传送门数量。
Example
Input
Copy
10
5 6
1 1 1 1 1
8 32
100 52 13 6 9 4 100 35
1 1
5
4 5
4 3 2 1
5 9
2 3 1 4 1
5 8
2 3 1 4 1
4 3
2 3 4 1
4 9
5 4 3 3
2 14
7 5
5 600000000
500000000 400000000 300000000 200000000 100000000
Output
Copy
2 3 0 1 3 2 1 1 2 2
在第一个测试用例中,您可以向右移动一个单位,使用索引为1的传送门,并传送到点n + 1,向左移动一个单位并使用索引为5的传送门。您剩下6-1-1-1-1 = 2个硬币,无论您传送到哪里,都没有足够的硬币使用另一个传送门。您已经使用了两个传送门,因此答案是2。
在第二个测试用例中,您向右移动四个单位并使用传送门前往n + 1,然后向左移动三个单位并使用索引为6的传送门前往n + 1,最后向左移动四次并使用传送门。总费用将是4+6+3+4+4+9 = 30,并且您使用了三个传送门。
在第三个测试用例中,您没有足够的硬币使用任何传送门,因此答案为零。
题解:
每个位置对答案的贡献最小是min(b[i] + i,b[i] + n - i + 1)
对其排序,f数组记录之前i现在所在位置,并记录前缀和
但是由于开始一定是从0开始的,
所以我们枚举一定会从0 ~ i使用传送门
如果可以在i位置传送,减去b[i] + i
然后看剩下的c,二分前缀和,记录位置pos
如果当前f[i] <= pos,说明有位置被重复计算了,c + min(b[i] + i,b[i] + n - i + 1),重新二分
否则答案取max(ans,pos + 1)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
#define int long long
typedef pair<int,int> PII;
typedef unsigned long long ULL;
const int N = 5e5 + 10;
int mod = 998244353;
struct node
{
int x,id;
}a[N];
bool cmp(node a,node b)
{
return a.x < b.x;
}
int f[N];
int b[N];
int pre[N];
int n,c;
int cal(int w)
{
int l = 0,r = n;
while(l <= r)
{
int mid = (l + r)/2;
if(pre[mid] > w)
{
r = mid - 1;
}
else
{
l = mid + 1;
}
}
return l - 1;
}
void solve()
{
cin >> n >> c;
for(int i = 1;i <= n;i++)
{
int x;
cin >> x;
b[i] = x;
a[i].x = min(x + i,x + n - i + 1);
a[i].id = i;
}
sort(a + 1,a + 1 + n,cmp);
for(int i = 1;i <= n;i++)
{
f[a[i].id] = i;
pre[i] = pre[i - 1] + a[i].x;
}
int ans = 0;
for(int i = 1;i <= n;i++)
{
int now = c;
if(b[i] + i > now)
continue;
now -= b[i] + i;
int pos = cal(now);
if(f[i] <= pos)
{
now += min(b[i] + i,b[i] + n - i + 1);
ans = max(ans,cal(now));
}
else
{
ans = max(ans,pos + 1);
}
}
cout << ans <<"\n";
}
signed main()
{
ios::sync_with_stdio(0 );
cin.tie(0);cout.tie(0);
int t = 1;
cin >> t;
while(t--)
{
solve();
}
}