2025 暑期 mx 集训 7.14

https://hydro.ac/d/pigsyy/p 来自 Pigsyy 的补题网址。

T1

https://www.mxoj.net/problem/P100313?contestId=29

题意

\(3\) 个杯子,只能进行一种操作:把一个杯子里的水倒到另一个杯子里去,直到另一个杯子满了,或这个被子空了就停止。

他们的容积分别是:\(A,B,C\) 满足 \(A\leq B \leq C \leq 10^5\),他们的初始水量是:\(a, b, c\)

不可以加新的水进来。对于 \(k = [0, C]\) 输出最少多少次操作可以使得任意一个杯子里的水恰好 \(= k\)

Solution

考虑没有新的水添加,那么由于每次操作必须有一个会倒满或倒空,那么其中一个为满 / 空的状态后,剩下两个的其中一个的状态也是唯一的,那最后一个的状态不确定。

所以总状态数 \(O(C)\),直接 bfs 就行。

tips:我这只能是感性理解,也可能不太对,但反正是能过的。

Code

#include <bits/stdc++.h>

using namespace std;

#define pii pair<int, pair<int, int>>

const int N = 3e5 + 10, inf = 0x3f3f3f3f;

int a[5], b[5], res[N];
map<pii, bool> mp;
pair<int, int> f[] = {{1, 2}, {1, 3}, {2, 1}, {2, 3}, {3, 1}, {3, 2}};

struct node { int a[5], d; };

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);
    freopen("juice.in", "r", stdin);
    freopen("juice.out", "w", stdout);
    cin >> b[1] >> b[2] >> b[3] >> a[1] >> a[2] >> a[3];
    memset(res, inf, sizeof res);
    res[a[1]] = res[a[2]] = res[a[3]] = 0;
    queue<node> q; q.push({{0, a[1], a[2], a[3]}, 0});
    auto get = [&](int a, int b, int c) -> pii {
        return make_pair(a, make_pair(b, c));
    };
    mp[get(a[1], a[2], a[3])] = 1;
    while (!q.empty()) {
        auto u = q.front(); q.pop();
        for (int i = 0; i < 6; i++) {
            int x = f[i].first, y = f[i].second;
            int o = min(u.a[x], b[y] - u.a[y]);
            if (o == 0) continue;
            u.a[x] -= o, u.a[y] += o;
            bool ok = (u.a[1] == 0 || u.a[1] == b[1] || u.a[2] == 0 || u.a[2] == b[2] || u.a[3] == 0 || u.a[3] == b[3]);
            if (!ok && !(u.a[1] == a[1] && u.a[2] == a[2] && u.a[3] == b[3])) continue;
            if (mp.find(get(u.a[1], u.a[2], u.a[3])) == mp.end()) {
                mp[get(u.a[1], u.a[2], u.a[3])] = 1;
                res[u.a[1]] = min(res[u.a[1]], u.d + 1);
                res[u.a[2]] = min(res[u.a[2]], u.d + 1);
                res[u.a[3]] = min(res[u.a[3]], u.d + 1);
                q.push({{0, u.a[1], u.a[2], u.a[3]}, u.d + 1});
            }
            u.a[x] += o, u.a[y] -= o;
        }
    }
    for (int i = 0; i <= b[3]; i++) cout << (res[i] == inf ? -1 : res[i]) << " \n"[i == b[3]];
    return 0;
}

T2

https://www.mxoj.net/problem/P100314?contestId=29

题意

你有 \(n\) 个物品,重为 \(w_i\),你有一个体积为 \(m\) 的背包,\(q\) 次询问从第 \(l\) 到第 \(r\) 个物品中,恰好重量恰好为 \(x\) 的方案数。对 \(10^9 + 7\) 取模。

\(n \leq 3\times 10^4, q \leq 3\times 10^5, m \leq 500\)

Solution

猫树板子。

但是可以用 cdq 分治做。

考虑先把询问挂到分治上。设当前询问区间为 \(l, r\),中点为 \(mid\)

询问为 \(ql, qr, x\)

首先我们只需要处理跨过 \(mid\) 的询问:

  • \(qr < mid\) 那把他扔到左区间。
  • \(ql > mid\) 扔到右区间。
  • 否则在当前这个区间处理。

对于当前这个区间,设 \(f_{i,j}\) 表示到了 \(i\),用了 \([i, mid]\) 这个后缀,此时背包容量为 \(j\) 的方案数。

同理,\(g_{i,j}\) 表示到了 \(i\)\([mid, i]\) 这段前缀,背包容量为 \(j\) 的方案数。

然后枚举询问合并答案即可。具体可看代码。

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

Code

#include <bits/stdc++.h>

using namespace std;

const int N = 3e4 + 10, Q = 3e5 + 10, P = 1e9 + 7;

int n, m, t, a[N];
int f[N][510], g[N][510], ans[Q];
struct qry { int l, r, x, id; };

void cdq(int l, int r, vector<qry> q)
{
    if (!q.size()) return;
    if (l == r) {
        for (auto u : q) ans[u.id] = (a[l] == u.x);
        return;
    }
    int mid = (l + r) / 2;
    vector<qry> q1, q2, q3;
    for (auto u : q) {
        if (u.r <= mid) q1.push_back(u);
        else if (u.l > mid) q2.push_back(u);
        else q3.push_back(u);
    }
    for (int i = mid + 1; i >= l; i--) for (int j = 0; j <= m; j++) f[i][j] = 0;
    for (int i = mid; i <= r; i++) for (int j = 0; j <= m; j++) g[i][j] = 0;
    f[mid + 1][0] = 1, g[mid][0] = 1;
    for (int i = mid; i >= l; i--) for (int j = 0; j <= m; j++) {
        f[i][j] = f[i + 1][j];
        if (j >= a[i]) (f[i][j] += f[i + 1][j - a[i]]) %= P;
    }
    for (int i = mid + 1; i <= r; i++) for (int j = 0; j <= m; j++) {
        g[i][j] = g[i - 1][j];
        if (j >= a[i]) (g[i][j] += g[i - 1][j - a[i]]) %= P;
    }
    for (auto u : q3) {
        int x = u.x;
        for (int i = 0; i <= x; i++) (ans[u.id] += 1ll * f[u.l][i] * g[u.r][x - i] % P) %= P;
    }
    cdq(l, mid, q1), cdq(mid + 1, r, q2);
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);
    freopen("seg.in", "r", stdin);
    freopen("seg.out", "w", stdout);
    cin >> n >> m >> t;
    for (int i = 1; i <= n; i++) cin >> a[i];
    vector<qry> q;
    for (int i = 1; i <= t; i++) {
        int l, r, x; cin >> l >> r >> x;
        q.push_back({l, r, x, i});
    }
    cdq(1, n, q);
    for (int i = 1; i <= t; i++) cout << ans[i] << "\n";
    return 0;
}

T3

https://www.mxoj.net/problem/P100315?contestId=29

题意

给你 \(n\) 个数 \(a_i\),定义一个函数 \(f(b)\)

假设你有一个序列 \(b\) 你可以对 \(b\) 进行以下操作:

选择一个 \(i\),然后删除 \(b_i\)\(b_{i + 1}\) 并添加一个 \(\gcd(b_i, b_{i + 1})\)

最终把 \(b\) 序列里的元素全变成同一元素的最小操作次数就是 \(f(b)\) 的值。

给你 \(q\) 次询问,每次询问求 \(f(a_{[l,r]})\)

\(n\leq 10^5, q\leq 3\times 10^5, a_i \leq 10^5\)

Solution

首先考虑最终所有数都会变成啥。

那就是变成这个区间的 \(\gcd\)

然后考虑不同的 \(\gcd\) 只有 \(n \log n\) 个。

因为你从每个 \(i\) 往后取 \(\gcd\) 只有 \(\log\) 个。所以总共 \(n\log n\) 个。

然后考虑设 \(z_{i,j,k}\) 表示从 \(i\) 开始,跳 \(2^j\) 步,在 \(\gcd = k\) 的情况下能最近能跳到哪。

为啥最近?因为相当于把这一段分成 \(2^j\) 块,块和块之间不需要合并,所以肯定想让块的大小尽可能小。

然后初始化就是从后往前扫,同时维护 \(\gcd\) 具体可看代码。

倍增因为你不确定都有哪些 \(\gcd\),所以要根据前面的来维护。

查询的话有一个 \(+1\),是因为你上一个块弄完了,所以你要跳到下一个块里去。同时防止 \(l = r\) 的块。

Code

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, inf = 0x3f3f3f3f;

int n, q, a[N], st[N][18], lg[N];
unordered_map<int, int> z[N][18];

int qry(int l, int r)
{
    int k = lg[r - l + 1];
    return __gcd(st[l][k], st[r - (1 << k) + 1][k]);
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);
    freopen("gcd.in", "r", stdin);
    freopen("gcd.out", "w", stdout);
    cin >> n >> q;
    lg[0] = -1;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        st[i][0] = a[i];
        lg[i] = lg[i / 2] + 1;
    }
    for (int j = 1; j <= 17; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            st[i][j] = __gcd(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
    
    for (int i = n; i >= 1; i--) {
        for (auto u : z[i + 1][0]) {
            int g = __gcd(u.first, a[i]);
            if (z[i][0][g]) z[i][0][g] = min(z[i][0][g], u.second);
            else z[i][0][g] = u.second;
        }
        z[i][0][a[i]] = i;
    }
    for (int j = 1; j <= 17; j++) {
        for (int i = 1; i <= n; i++) {
            for (auto u : z[i][j - 1]) {
                if (!u.second || !z[u.second + 1][j - 1][u.first]) continue;
                z[i][j][u.first] = z[u.second + 1][j - 1][u.first];
            }
        }
    }
    for (int i = 1; i <= q; i++) {
        int l, r; cin >> l >> r;
        int g = qry(l, r), ans = r - l + 1, x = l;
        for (int j = 17; j >= 0; j--) {
            if (z[x][j][g] && z[x][j][g] <= r) {
                x = z[x][j][g] + 1;
                ans -= (1 << j);
            }
        }
        cout << ans << "\n";
    }
    return 0;
}

T4

https://www.luogu.com.cn/problem/P5806

大模拟,扔了。

T5

https://www.luogu.com.cn/problem/CF1569F

T6

hdu7200

posted @ 2025-08-04 11:10  Dtwww  阅读(130)  评论(0)    收藏  举报