codeforces1490 G. Old Floppy Drive(思维)

题意:圆盘上有 n 个数,起初指针在第一个位置,m 次询问,每次询问回答指针至少移动多少次使得指针所经过的所有数的和 >= x(指针不可反向)

思路:

维护一个递增的前缀和 b[ ],并记录达到该前缀和的位置 id[ ]。

(1)判断是否存在,若x > 前缀和的最大值且整个数组的和 <= 0,则不存在。

(2)如果第一轮就可以找到 >= x 的数,直接lower_bound;如果需要 k 轮,并且最后加的一个数为b[i],即求一对 i,k 满足 k * sum + b[i] >= x,且使 id[i] - 1 + k * n最小,显然应该先让k取最小,就是用最大的 b[i] 来求最小的 k ,所以

k = \left \lfloor \frac{x - b[tot]}{sum} \right \rfloor,tot为严格递增的前缀和的个数,再lower_bound即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 2e5 + 7;

ll a[N], b[N], id[N];

int main() {
    ll t, n, m, x;
    scanf("%lld", &t);
    while(t--) {
        scanf("%lld%lld", &n, &m);
        ll sum = 0, maxx = 0, tot = 0;
        b[0] = 0;
        for(ll i = 1; i <= n; ++i) {
            scanf("%lld", &a[i]);
            sum += a[i];
            maxx = max(maxx, sum);
            if(sum > b[tot]) b[++tot] = sum, id[tot] = i;
        }
        for(ll i = 1; i <= m; ++i) {
            scanf("%lld", &x);
            if(i > 1) printf(" ");
            if(x > maxx && sum <= 0) {
                printf("-1");
                continue;
            }
            if(x <= a[1]) {
                printf("0");
                continue;
            }
            if(x > maxx) {
                ll cnt = (x - b[tot] + sum - 1) / sum;
                ll it = lower_bound(b + 1, b + tot + 1, x - cnt * sum) - b;
                printf("%lld", n * cnt + id[it] - 1);
            }
            else {
                ll it = lower_bound(b + 1, b + tot + 1, x) - b;
                printf("%lld", id[it] - 1);
            }
        }
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43871207/article/details/113836742
今日推荐