11.20 C 盲盒流水线

题解 : 盲盒流水线

link

给定 \(n\) 个物品,每个物品有价值和质量,\(q\) 次询问,在 \([l , r]\) 中选质量不超过 \(m\) 的物品,每个物品至多选一次,并且要让价值最大

$ 1 \leq n \leq 20000$
$ 1 \leq m_i \leq 500$
$ 1 \leq q \leq 100000$

第一眼,这不是背包吗?

暴力:\(O(nmq)\) \(49pts\)

const int N = 2e4 + 20, mod = 998244353;
int n, v[N], w[N], q, g[N], f[N], p[N];
signed main(){ 
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; ++i)cin >> v[i] >> w[i];
    cin >> q;
    for(int qwq = 1, l, r, m, cnt = 0; qwq <= q; ++qwq, cnt = 0){
        cin >> l >> r >> m;
        g[0] = 1;
        for(int i = l; i <= r; ++i){
            for(int j = m; j >= w[i]; --j){
                if(f[j] < f[j - w[i]] + v[i])f[j] = f[j - w[i]] + v[i], g[j] = g[j - w[i]] % mod ;
                else if(f[j] == f[j - w[i]] + v[i])f[j] = f[j - w[i]] + v[i], (g[j] += g[j - w[i]]) %= mod;   
            }
        }
        for(int i = 1; i <= m; ++i)(cnt += (f[i] == f[m]) * g[i] % mod) %= mod;
        cout << f[m] << ' ' << cnt << '\n';
        for(int i = 0; i <= m; ++i)f[i] = g[i] = 0;
    } 
    return 0;
}

然后可以上一些数据结构? 本机房大蛇bjt赛时似乎用一些数据结构优化到了 \(66pts\), 但是我没看懂

我们发现,这个区间会被反复查询导致有重复,由于背包只能加不能删,所以赛时我用回滚莫队优化 \(dp\),单次移动指针是 \(O(m)\) 的,最终复杂度 \(O(n m \sqrt q)\), 可以获得 \(83pts\)

机房大佬 洛阳民宿的 tj

赛时代码:

const int M = 510, Q = 1e5 + 20, N = 2e4 + 20, mod = 998244353;
int n, qwq, v[N], w[N], B, cnt, L[N], R[N], bel[N], z, g[M], f[M], f_[M], g_[M], Mx[Q];
struct Que{
    int l, r, m, id;
}q[Q];
pair<int,int> ans[Q];
vector<Que>G[Q];
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; ++i)cin >> v[i] >> w[i];
    cin >> qwq;
    B = n / (sqrt(qwq) + 1) + 1, cnt = n / B;
    for(int i = 1; i <= cnt; ++i)L[i] = (i - 1) * B + 1, R[i] = B * i;
    if(R[cnt] < n)++cnt, L[cnt] = R[cnt - 1] + 1, R[cnt] = n;
    for(int i = 1; i <= cnt; ++i)for(int j = L[i]; j <= R[i]; ++j)bel[j] = i;
    for(int i = 1, l_, r_, m_; i <= qwq; ++i){
        cin >> l_ >> r_ >> m_;
        if(bel[l_] == bel[r_]){
            g[0] = 1;int as = 0, zz = 0;
            for(int i = l_; i <= r_; ++i){
                for(int j = m_; j >= w[i]; --j){
                    if(f[j] < f[j - w[i]] + v[i])f[j] = f[j - w[i]] + v[i], g[j] = g[j - w[i]] % mod ;
                    else if(f[j] == f[j - w[i]] + v[i])f[j] = f[j - w[i]] + v[i], (g[j] += g[j - w[i]]) %= mod;   
                    zz = f[zz] < f[j] ? j : zz;
                }
            }
            for(int i = 1; i <= m_; ++i)(as += (f[i] == f[zz]) * g[i] % mod) %= mod;
            ans[i] = {f[zz], as}; 
            for(int i = 0; i <= m_; ++i)f[i] = g[i] = 0;
        }   
        else G[bel[l_]].push_back({l_, r_, m_, i}), Mx[bel[l_]] = max(m_, Mx[bel[l_]]);
    }
    for(int i = 1; i <= cnt; ++i)
        if(G[i].size())sort(G[i].begin(), G[i].end(), [](Que a, Que b){return a.r < b.r;});
    for(int b = 1; b <= cnt; ++b){
        if(!G[b].size())continue;
        memset(g, 0, sizeof(g));memset(f, 0, sizeof(f));
        g[0] = 1;
        int l_ = R[b], r_ = R[b] + 1, zz = 0;
        int mm = Mx[b];
        for(auto [l, r, m, id] : G[b]){
            int as = 0;
            for(int i = r_; i <= r; i++){
                for(int j = mm; j >= w[i]; --j){
                    if(f[j] < f[j - w[i]] + v[i])f[j] = f[j - w[i]] + v[i], g[j] = g[j - w[i]] % mod ;
                    else if(f[j] == f[j - w[i]] + v[i])f[j] = f[j - w[i]] + v[i], (g[j] += g[j - w[i]]) %= mod;   
                }
            }
            r_ = r + 1;
            for(int i = 0; i <= mm; ++i)f_[i] = f[i], g_[i] = g[i]; 
            for(int i = l_; i >= l; --i){
                for(int j = mm; j >= w[i]; --j){
                    if(f_[j] < f_[j - w[i]] + v[i])f_[j] = f_[j - w[i]] + v[i], g_[j] = g_[j - w[i]] % mod ;
                    else if(f_[j] == f_[j - w[i]] + v[i])f_[j] = f_[j - w[i]] + v[i], (g_[j] += g_[j - w[i]]) %= mod;   
                }
            }
            for(int i = 1; i <= m; ++i)(as += (f_[i] == f_[m]) * g_[i] % mod) %= mod;
            ans[id] = {f_[m] , as};
            for(int i = 0; i <= mm; ++i)f_[i] = g_[i] = 0;
        }
    }
    for(int i = 1; i <= qwq; ++i)cout << ans[i].first << ' ' << ans[i].second << '\n';   
    return 0;
}

由于没有修改操作,我们考虑猫树分治

其实在很早之前一场abc,我了解过猫树分治的说,但是我把他放到任务清单里后就没有再理他。。。

ABC426G,P6240 都是原题

算法大概是这样的:

我们选定当前区间 \([l, r]\)\(mid\) ,从 \(mid\) 向前向后预处理处理到边界,将左右边界在这个区间内的询问进行处理答案

合并两个背包是 \(O(n^2)\) 的,但是我们单纯统计答案,只需要 res = max(res, L[b[i].l][j] + R[b[i].r][b[i].m - j]) 就可以 \(O(m)\) 的时间内统计答案

时间复杂度应该是 \(T(n) = 2T(n / 2) + O(n) = O(n \log n)\)

总时间复杂度是 \(O(nm \log n + qm)\)

关于初始化,我一开始发现将 只将 GL[mid + 1][0]GR[mid][0] 设成\(1\),发现答案会小,把 GL[mid + 1] GR[mid] 都设成 \(1\), 发现答案会变大

然后,我将 GL[mid + 1][0]GR[mid] 设成 \(1\) 就过了,我自己的解释就是说,我们对于左边的只取恰好的,右边的取小于等于的,这样每一种方案只会被统计一次

const int mod = 998244353, N = 2e4 + 20, Q = 1e5 + 20, M = 502;
int n, v[N], w[N], qwq, L[N][M + 20], R[N][M + 20], GL[N][M + 20], GR[N][M + 20];
pair<int, int>ans[Q];
struct Que{
    int l, r, m, id;
}q[Q], b[Q]; 
void solve(int l, int r, int ql, int qr){
    int mid = (l + r) >> 1;
    if(ql > qr)return;
    for(int i = 0; i <= M + 5; ++i)L[mid + 1][i] = 0, GL[mid + 1][i] = !i; 
    for(int i = mid; i >= l; --i){
        for(int j = 0; j <= M; j++){
            GL[i][j] = GL[i + 1][j], L[i][j] = L[i + 1][j];
            if(j >= w[i]){
                if(L[i + 1][j - w[i]] + v[i] > L[i][j])
                    L[i][j] = L[i + 1][j - w[i]] + v[i], GL[i][j] = GL[i + 1][j - w[i]];
                else if(L[i + 1][j - w[i]] + v[i] == L[i][j])(GL[i][j] += GL[i + 1][j - w[i]]) %= mod;
            }
        }
    }
    for(int i = 0; i <= M + 5; ++i)R[mid][i] = 0, GR[mid][i] = 1;
    for(int i = mid + 1; i <= r; ++i){
        for(int j = 0; j <= M; ++j){
            GR[i][j] = GR[i - 1][j], R[i][j] = R[i - 1][j];
            if(j >= w[i]){
                if(R[i - 1][j - w[i]] + v[i] > R[i][j])
                    R[i][j] = R[i - 1][j - w[i]] + v[i], GR[i][j] = GR[i - 1][j - w[i]];
                else if(R[i - 1][j - w[i]] + v[i] == R[i][j])(GR[i][j] += GR[i - 1][j - w[i]]) %= mod;
            }
        }
    }
    int nql = ql, nqr = qr;
    for(int i = ql; i <= qr; ++i)b[i] = q[i];
    for(int i = ql; i <= qr; ++i){
        if(b[i].r < mid)q[nql++] = b[i]; 
        else if(b[i].l > mid)q[nqr--] = b[i];
        else{
            int res = 0, as = 0; 
            for(int j = 0; j <= b[i].m; ++j)
                res = max(res, L[b[i].l][j] + R[b[i].r][b[i].m - j]);
            for(int j = 0; j <= b[i].m; ++j){
                if(res == L[b[i].l][j] + R[b[i].r][b[i].m - j]){
                    (as += 1ll * GL[b[i].l][j] * GR[b[i].r][b[i].m - j] % mod) %= mod;
                }
            }
            ans[b[i].id] = {res, as};
        }
    }
    solve(l, mid, ql, nql - 1), solve(mid + 1, r, nqr + 1, qr);
}
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; ++i)cin >> v[i] >> w[i];
    cin >> qwq;
    for(int i = 1; i <= qwq; ++i)cin >> q[i].l >> q[i].r >> q[i].m, q[i].id = i;
    solve(1, n, 1, qwq);
    for(int i = 1; i <= qwq; ++i){
        if(ans[i].first)cout << ans[i].first << ' ' << ans[i].second << '\n';
        else cout << 0 << ' ' << 0 << '\n';
    }
    return 0;
}
posted @ 2025-11-21 07:41  Nailong2357  阅读(65)  评论(6)    收藏  举报