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;
}
posted @ 2022-07-03 21:49  qingyanng  阅读(111)  评论(0编辑  收藏  举报