询问算法正确性
查看原帖
询问算法正确性
817044
cjwdyzxfblzs楼主2023/11/9 11:32

我简单说一下我的思路:

先 O(n)O(n) 预处理出来前缀和。

在每一次询问的时候:从第一个位置开始,预处理到第2n个位置,如果当前区间的值大于了k,就记录好这个区间,大概就是这样子的。

if (now + a[i] > k)
{
    ++ cnt;
    st[cnt] = lst, ed[cnt] = i - 1;
    lst = i, now = 0;
}

然后这样每一个点都被唯一的包含在了一个块中。

在查询的时候,就类似分块。O(n)O(n)枚举 ii 到 i+n−1i +n - 1 这个区间里面有多少个块,如果开头的散块和最后的散块的权值相加 ≤k\le k就算成一个块。

我在提交的时候WA on #21,有点懵逼,不知道我这个算法是否正确,或者如果不正确哪里可以优化一下的

#include <bits/stdc++.h>
#define int long long
inline int rd(void) 
{
    int x = 0, f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
    return x * f;
}
const int N = 3e6 + 10;
int n, q, a[N], sum[N];
int bel[N], st[N], ed[N];
int pos[N];
auto main() -> signed
{
    n = rd(), q = rd();
    for (int i = 1; i <= n; i ++ ) {
        a[i] = rd();
        a[n + i] = a[i];
    }
    for (int i = 1; i <= 2 * n; i ++ ) {
        sum[i] = sum[i - 1] + a[i];
    }
    while (q -- )
    {
        int k = rd();
        int lst = 1, now = 0, cnt = 0;
        for (int i = 1; i <= 2 * n; i ++ ) {
            if (now + a[i] > k)
            {
                ++ cnt;
                st[cnt] = lst, ed[cnt] = i - 1;
                lst = i, now = 0;
            }
            now += a[i];
        }
        if (now) {
            ++ cnt;
            st[cnt] = lst, ed[cnt] = 2 * n;
        }
        for (int i = 1; i <= cnt; i ++ ) {
            for (int j = st[i]; j <= ed[i]; j ++ )
                bel[j] = i;
        }

        int min = 0x3f3f3f3f;
        for (int i = 1; i <= n; i ++ ) {
            int l = i, r = i + n - 1;
            int bell = bel[l] + 1, belr = bel[r] - 1;
            int ans = std::max(belr - bell + 1, 0ll);
            if (sum[ed[bel[l]]] - sum[l - 1] + sum[r] - sum[st[bel[r]] - 1] <= k)
                ans ++ ;
            else ans += 2;
            min = std::min(ans, min);
        }
        std::cout << min << std::endl;
    }
    return 0;
}
2023/11/9 11:32
加载中...