abc258_e Packing Potatoes (循环节, 二分)
题意:给出无限长的土豆序列,第i个重量为 \(w_{(i-1)mod n}\), 从第一个土豆开始进行打包,重量和大于等于x的最少土豆为一包。 查询q次第k个包中有多少土豆。
思路:
一包有n个可能的起点,一个起点的终点是固定的。所以最多有n种包,根据鸽巢原理,最多包n包,出现循环。所以二分出每个wi做起点的终点。然后跑n包找出循环节。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define PII pair<int, char>
//#define int long long
const int N = 2e5 + 5;
const int M = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
ll n, x;
ll w[N], ne[N], cnt[N], ans[N], re[N]; ll pre[N]; bool st[N];
ll get( ll id ) {
ll res = 0;
res += pre[n] * (id / n) + pre[id % n];
return res;
}
int main() {
IOS
ll q; cin >> n >> q >> x;
for ( int i = 0; i < n; ++ i ) {
cin >> w[i];
pre[i + 1] = pre[i] + w[i];
}
for ( int i = 0; i < n; ++ i ) {
ll l = i + 1, r = i + x;
while( l < r ) {
ll mid = l + r >> 1;
if ( get(mid) - pre[i] >= x ) r = mid;
else l = mid + 1;
}
ne[i] = l % n; cnt[i] = l - i;
}
ll sz = 0;
ll limit = 0; ll j = 0;
for ( int i = 0; ; i = ne[i] ) {
if( st[i] == 1 ) {
sz = j - re[i] + 1; limit = re[i] - 1; break;
}
st[i] = 1;
++ j;
ans[j] = cnt[i]; re[i] = j;
}
while( q -- ) {
ll k; cin >> k;
if( k <= limit ) cout << ans[k] << '\n';
else cout << ans[(k - limit - 1) % sz + limit + 1] << '\n';
}
return 0;
}