2024 ICPC成都

The 2024 ICPC Asia Chengdu Regional Contest

外榜 https://board.xcpcio.com/icpc/49th/chengdu

铜线: 5题953-331 ~ 6题770

银线: 6题769-456 ~ 7题674

金线: 7题600-600 8题+

难度(个人感觉):

纯签: LJ

半签: GA

Easy: I

Mid: B

L

题意

有50%的数小于等于a,95%的数小于等于b,99%的数小于等于c。

输出构造的序列。

思路

可以50个a,95-50个b,99-95个c,100-99个大于c的。

没啥好说的,就是读题麻烦点。

代码

void solve() {
    int a, b, c;
    cin >> a >> b >> c;
    int n = 100;
    cout << n << '\n';
    for (int i = 1; i <= 50; i++) {
        cout << a << " ";
    }
    for (int i = 51; i <= 95; i++) {
        cout << b << " ";
    }
    for (int i = 96; i <= 99; i++) {
        cout << c << " ";
    }
    cout << c + 1 << '\n';
}

J

模拟

代码

void solve() {
    int n, m, q;
    cin >> n >> m >> q;

    std::map<int, bool> vis;
    std::vector<std::pair<int, int>> ans(m + 1);
    for (int i = 0; i <= m; i++) {
        ans[i] = {i, 0};
    }
    int now = 0, sum = m;
    while (q--) {
        int op, id, x;
        cin >> op;
        if (op == 1) {
            cin >> x;
            now = x;
            vis.clear();
            sum = m;
        } else if (op == 2) {
            cin >> id >> x;
            if (!vis[id] && now == x) {
                vis[id] = 1;
                ans[id].second += sum--;
            }
        } else {
            cin >> id >> x;
            if (now == x) {
                vis[id] = 1;
            }
        }
    }
    sort(ans.begin() + 1, ans.end(), [&] (auto x, auto y) {
        return x.second == y.second ? x.first < y.first : x.second > y.second;
    });
    for (int i = 1; i <= m; i++) {
        cout << ans[i].first << " " << ans[i].second << "\n";
    }
}

G

题意

对数组\(a\)可以执行任意次操作,每次在\(i\)\(i+1\)之间插入\(a_i \land a_{i+1}\)或者\(a_i \lor a_{i+1}\)或者\(a_i \oplus a_{i+1}\)

求数组中最多可以有多少不同的数。

思路

位运算多次也没有太大的意义,最多两次异或可以产生\(0\)

所以使劲往里插,相邻两个数中间插入的只会受这两个数的影响,把他俩中间能插的全插进去。

代码

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    std::set<int> set;
    set.insert(0);
    for (int i = 1; i < n; i++) {
        int l = a[i], r = a[i + 1];
        set.insert(l);
        set.insert(r);
        vector<int> res = {l ^ r, l & r, l | r};
        for (int j = 0; j < 3; j++) {
            res.push_back(l ^ res[j]);
            res.push_back(l & res[j]);
            res.push_back(l | res[j]);
            res.push_back(r ^ res[j]);
            res.push_back(r & res[j]);
            res.push_back(r | res[j]);
        }
        for (auto x : res) {
            set.insert(x);
        }
    }
    cout << set.size() << endl;
}

A

题意

操作长度为\(n\)的空字符串,每次操作可以选择长度至少为5的子串,改成箭头串,问能不能\(n\)次操作内变成s,输出每次操作的区间或者不能。

箭头串: 长度至少为5,'>'开头,'>>>'结尾,中间都是'-'。

思路

开头不是'>'结尾不是">>>"或者全'>'的一定不能。

把后面连续的'>'用长度为5的填满。

从左往右,每次以'>'为开头到串尾第一次连续三个'>'为结尾进行操作。

代码

void solve() {
    string s;
    cin >> s;

    int n = s.size();
    int len = std::count(all(s), '>');
    if (len == n || s[0] == '-' || s.substr(n - 3, 3) != ">>>") {
        cout << "No\n";
        return;
    }
    int p = n - 1;
    for (int i = n - 1; i >= 2; i--) {
        if (s.substr(i - 2, 3) == ">>>") {
            p = i;
        } else {
            break;
        }
    }
    cout << "Yes ";
    vector<std::pair<int, int>> res;
    for (int i = n - 1; i >= p; i--) {
        res.push_back({i - 3, 5});
    }
    for (int i = 0; i < p - 2; i++) {
        if (s[i] == '>') {
            res.push_back({i + 1, p - i + 1});
        }
    }
    cout << res.size() << '\n';
    for (auto [l, r] : res) {
        cout << l << " " << r << "\n";
    }
}

I

题意

一种划分\(k\)是合法的,当且仅当划分成连续的\(\lceil \frac{n}{k} \rceil\)部分,每部分的长度为\(1 \le k \le n\)(最后一部分可以不够),且非递减。

\(q\)次修改,将\(a_p\)改成\(v\)。求每次修改后有多少种划分合法。

思路

如果对于划分x合法,那么对于x的因子也合法,就相当于把原来的一部分又继续细分了。

所以维护好断开位置下标的gcd,只要找到最长划分,答案就是因子数量。

代码

void solve() {
    int n, q;
    cin >> n >> q;
    vector<int> a(n + 2);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    vector<int> fac(n + 1);
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j += i) {
            fac[j]++;
        }
    }

    fac[0] = n;
    vector<int> tr(n << 2);

    auto pull = [&] (int i) -> void {
        tr[i] = std::gcd(tr[i << 1], tr[i << 1 | 1]);
    };

    auto modify = [&] (auto &&modify, int i, int l, int r, int p, int v) -> void {
        if (l == r) {
            tr[i] = v;
            return;
        }
        int mid = l + r >> 1;
        if (p <= mid)
            modify(modify, i << 1, l, mid, p, v);
        if (p > mid)
            modify(modify, i << 1 | 1, mid + 1, r, p, v);
        pull(i);
    };
    for (int i = 1; i < n; i++) {
        if (a[i] > a[i + 1]) {
            modify(modify, 1, 1, n, i, i);
        } else {
            modify(modify, 1, 1, n, i, 0);
        }
    }

    cout << fac[tr[1]] << '\n';
    while (q--) {
        int p, v;
        cin >> p >> v;
        a[p] = v;
        if (a[p - 1] > a[p]) {
            modify(modify, 1, 1, n, p - 1, p - 1);
        } else {
            modify(modify, 1, 1, n, p - 1, 0);
        }
        if (p + 1 <= n) {
            if (a[p] > a[p + 1]) {
                modify(modify, 1, 1, n, p, p);
            } else {
                modify(modify, 1, 1, n, p, 0);
            }
        }
        cout << fac[tr[1]] << '\n';
    }
}

B

题意

有三种字符abc,要求构造长度为\(n\)的字符串,把原串里的'?'换成abc且相邻两个不相同,问能构造出多少种字符串。

\(q\)次询问,特殊的,每次给\(x,y,z\)表示最多可以额外有\(x\)个a,\(y\)个b,\(z\)个c,输出答案。

思路

因为\(n,x,y,z \le 300\),所以dp预处理出所有的对应xyz的情况。

这样每次查询就把dp累加统计起来就可以了,时间复杂度\(O(q \times xyz)\),不能接受,高维前缀和维护一下就可以每次\(O(1)\)查询。

代码

constexpr int mod = 1e9 + 7;

ll dp[305][305][305][3]; // 前 i 个位置 额外使用 j个a, k个b,当前位置使用字母 l+'a';
ll f[305][305][305];
void solve() {
    int n, q;
    cin >> n >> q;
    string s;
    cin >> s;
    s = " " + s;
    vector<int> pre(n + 1);
    for (int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1] + (s[i] == '?');
    }
    if (s[1] == '?') {
        dp[1][1][0][0] = 1;
        dp[1][0][1][1] = 1;
        dp[1][0][0][2] = 1;
    } else {
        dp[1][0][0][s[1] - 'a'] = 1;
    }
    for (int i = 2; i <= n; i++) {
        for (int a = 0; a <= pre[i]; a++) {
            for (int b = 0; a + b <= pre[i]; b++) {
                if (s[i] == '?') {
                    if (a) dp[i][a][b][0] = dp[i - 1][a - 1][b][1] + dp[i - 1][a - 1][b][2];
                    if (b) dp[i][a][b][1] = dp[i - 1][a][b - 1][0] + dp[i - 1][a][b - 1][2];
                    if (pre[i] - a - b) dp[i][a][b][2] = dp[i - 1][a][b][0] + dp[i - 1][a][b][1];
                } else {
                    dp[i][a][b][s[i] - 'a'] = dp[i - 1][a][b][0] + dp[i - 1][a][b][1] + dp[i - 1][a][b][2] - dp[i - 1][a][b][s[i] - 'a'];
                }
            }
        }
        for (int a = 0; a <= pre[i]; a++) {
            for (int b = 0; a + b <= pre[i]; b++) {
                for (int j = 0; j < 3; j++) {
                    dp[i][a][b][j] %= mod;
                }
            }
        }
    }
    for (int a = 0; a <= pre[n]; a++) {
        for (int b = 0; a + b <= pre[n]; b++) {
            int c = pre[n] - a - b;
            f[a][b][c] = (dp[n][a][b][0] + dp[n][a][b][1] + dp[n][a][b][2]) % mod;
        }
    }
    for (int i = 0; i <= 300; i++) {
        for (int j = 0; j <= 300; j++) {
            for (int k = 0; k <= 300; k++) {
                if (i) f[i][j][k] += f[i - 1][j][k];
                if (j) f[i][j][k] += f[i][j - 1][k];
                if (k) f[i][j][k] += f[i][j][k - 1];
                if (i && j) f[i][j][k] -= f[i - 1][j - 1][k];
                if (i && k) f[i][j][k] -= f[i - 1][j][k - 1];
                if (j && k) f[i][j][k] -= f[i][j - 1][k - 1];
                if (i && j && k) f[i][j][k] += f[i - 1][j - 1][k - 1];
                f[i][j][k] %= mod;
            }
        }
    }
    while (q--) {
        int x, y, z;
        cin >> x >> y >> z;
        cout << (f[x][y][z] + mod) % mod << "\n";
    }
}
posted @ 2025-08-18 14:55  sunjiànqiáng  阅读(30)  评论(0)    收藏  举报