题意:n 个传送门,只能用一次,一次花费 ai 个硬币,使用了能转移到 0 或者 n + 1,你一开始在 0 点,往左往右移动一步都要花费 1 个硬币,问最多可以使用几个传送门;
分析:首先发现每个点实际的贡献就是(min(a[i] + i, a[i] + n - i + 1)),但是不能直接排序算,因为我们必须从 0 号点出发,第一个位置是独立的,思考了一会没啥办法,无奈只能将这一维枚举,再考虑如何用 logn 的时间复杂度算出每个起点的最大个数,一开始想着用个堆维护一下,但复杂度太高,再想着离线,排序之后,再利用单调性二分再降复杂度,直接排序后前缀和处理好直接二分。然后再特判一下开头点是否在二分后的区间就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+15;
const int mo = 998244353;
#define pb push_back
#define pii pair<int,int>
#define ft first
#define sd second
#define ffor(i,a,b,c) for(int i=(a);i<(b);i+=(c))
#define For(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define rfor(i,a,b,c) for(int i=(a);i>(b);i-=(c))
#define Rfor(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define all(x) (x).begin(), (x).end()
#define debug1(x) cerr<<"! "<<x<<endl;
#define debug2(x,y) cerr<<"# "<<x<<" "<<y<<endl;
void slv(){
int n; ll c; cin >> n >> c;
vector<int> a(n + 1);
vector<ll> pr(n + 1), mn(n + 1);
vector<pii> cst(1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
cst.pb({min(a[i] + i, a[i] + n - i + 1), i});
mn[i] = min(a[i] + i, a[i] + n - i + 1);
}
sort(cst.begin(), cst.end());
vector<int> mp(n + 1);
for (int i = 1; i <= n; i++) {
mp[cst[i].sd] = i;
}
for (int i = 1; i <= n; i++) {
pr[i] = pr[i - 1] + cst[i].ft;
}
auto cal = [&] (ll k) {
int l = 0, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
// debug1(mid)
if(pr[mid] <= k)
l = mid;
else
r = mid - 1;
}
return l;
};
int sm = 0;
for (int i = 1; i <= n; i++) {
int res = c;
if(a[i] + i > res) continue;
res -= (a[i] + i);
int pos = cal(res);
if(mp[i] <= pos) {
res += mn[i];
sm = max(sm, cal(res));
} else {
sm = max(sm, pos + 1);
}
}
cout << sm << '\n';
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int t;cin>>t;
while(t--){
slv();
}
}