杂题
2025-09-22
暂无标签

最近的几场 abc, arc, cf, 模拟赛题


#G - Set list

https://atcoder.jp/contests/abc424/tasks/abc424_g


#B - Slime Swap

https://atcoder.jp/contests/arc206/tasks/arc206_b


#D - LIS ∩ LDS

https://atcoder.jp/contests/arc206/tasks/arc206_d


##3761. Bubble Sort

http://poj.org/problem?id=3761


#F. Bubble Sort

https://codeforces.com/contest/2146/problem/F


#E. Limited Edition Shop

https://codeforces.com/contest/2151/problem/E

  • 构造几个 n=2,3 的例子手玩一下,会发现对于 a,b 两个元素,如果在 A 里的相对顺序是 ab,在 B 里的相对顺序是 ba,那么要选 b 就必须选 a。
  • 那么会想到把 A 变成 1n,那么对于 B 中的一个元素,如果其后方有更小的元素,那么必须选了它才能选这个数。
  • 考虑在 B 中 DP 最后选出来的集合,那么对于当前的 x若不选,则不允许『已选集合』中存在 >x 的数。
  • 故令 fi,j 表示 DP 到 i,当前已选集合最大值为 j 的最大集合大小,那么有:

    fi,Bifi1,j+v,j<Bifi,jfi1,j+v,j>Bifi,jfi1,j,j<Bi

  • 显然可以线段树优化转移

#include <bits/stdc++.h>
const int maxn = 2e5 + 5;
const long long inf = 1e18;
struct {
    int l, r;
    long long u, d;
} t[maxn << 2];
#define lt (p << 1)
#define rt (lt | 1)
void pushval(int p, long long v) {
    t[p].d += v, t[p].u += v;
    return;
}
void pushdown(int p) {
    pushval(lt, t[p].d), pushval(rt, t[p].d);
    t[p].d = 0ll;
    return;
}
void pushup(int p) {
    t[p].u = std::max(t[lt].u, t[rt].u);
    return;
}
void bld(int p, int l, int r) {
    t[p].l = l, t[p].r = r;
    t[p].u = -inf, t[p].d = 0ll;
    if (l == r)
        return;
    int mid = (t[p].l + t[p].r) >> 1;
    bld(lt, l, mid), bld(rt, mid + 1, r);
    return;
}
void upd(int p, int x, long long v) {
    t[p].u = std::max(t[p].u, v);
    if (t[p].l == t[p].r)
        return;
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if (x <= mid)
        upd(lt, x, v);
    else
        upd(rt, x, v);
    return;
}
void add(int p, int l, int r, long long v) {
    if (l <= t[p].l && t[p].r <= r) {
        pushval(p, v);
        return;
    }
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if (l <= mid)
        add(lt, l, r, v);
    if (r > mid)
        add(rt, l, r, v);
    pushup(p);
    return;
}
long long ask(int p, int l, int r) {
    if (l <= t[p].l && t[p].r <= r)
        return t[p].u;
    pushdown(p);
    long long res = -inf;
    int mid = (t[p].l + t[p].r) >> 1;
    if (l <= mid)
        res = ask(lt, l, r);
    if (r > mid)
        res = std::max(res, ask(rt, l, r));
    return res;
}
int main() {
#ifdef ONLINE_JUDGE
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
#else
    std::freopen(".in", "r", stdin);
    std::freopen(".out", "w", stdout);
    const auto stime = std::chrono::steady_clock::now();
#endif
    int T;
    for (std::cin >> T; T--; ) {
        int n;
        std::cin >> n;
        std::vector<int> w(n + 1), k(n + 1), a(n + 1), b(n + 1), tab(n + 1);
        for (int i = 1; i <= n; ++i)
            std::cin >> w[i];
        for (int i = 1; i <= n; ++i)
            std::cin >> a[i], tab[a[i]] = i;
        for (int i = 1; i <= n; ++i)
            k[i] = w[a[i]];
        for (int i = 1; i <= n; ++i)
            std::cin >> b[i], b[i] = tab[b[i]];
        bld(1, 0, n), upd(1, 0, 0);
        for (int i = 1; i <= n; ++i) {
            if (b[i] != n)
                add(1, b[i] + 1, n, k[b[i]]);
            upd(1, b[i], ask(1, 0, b[i] - 1) + k[b[i]]);
        }
        std::cout << t[1].u << '\n';
    }
#ifndef ONLINE_JUDGE
    std::cerr << std::fixed << std::setprecision(6) << std::chrono::duration<double> (std::chrono::steady_clock::now() - stime).count() << "s\n";
#endif
    return 0;
}
查看代码

#A. 小猪盖房子

http://222.180.160.110:61235/problem/51898

给定一个 n×m 的 01 矩阵,每一行有 2 个 1 或没有 1。现需选择两个相邻的行区间,并为范围内的空行选择两个位置改为 1,使得这两个区间是全等的。

求可能的方案。当空行的涂色方式不同或选择范围不同看作不同的方案。对 998244353 取模。

n,m5000

  • 第一反应是枚举分界线 + 上端点,发现没有办法做:当上端点移动的时候,下部矩形会整体平移。
  • 进一步考虑导致这个问题的原因,感受到一个因素是长度在变。
  • 故固定长度枚举,前缀和统计 m(m1)2 的幂次即可。
#include <bits/stdc++.h>
const int mod = 998244353;
int main() {
    std::freopen("piggy.in", "r", stdin);
    std::freopen("piggy.out", "w", stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
    const auto stime = std::chrono::steady_clock::now();
    int n, m;
    std::cin >> n >> m;
    std::vector<std::array<int, 2> > a(n + 1);
    std::vector<long long> ps(n + 1);
    const int s = m * (m - 1) / 2;
    ps[0] = 1ll;
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i][0] >> a[i][1];
        ps[i] = ps[i - 1] * s % mod;
    }
    auto res = 0ll;
    for (int i = 1; i <= n / 2; ++i) {
        std::vector<int> cnt0(n + 1), cnts(n + 1);
        for (int u = 1; u <= n - i; ++u) {
            int d = u + i;
            if (!a[u][0] && !a[d][0])
                cnts[u] = 1;
            else if (a[u][0] && a[d][0] && (a[u][0] != a[d][0] || a[u][1] != a[d][1]))
                cnt0[u] = 1;
            cnts[u] += cnts[u - 1], cnt0[u] += cnt0[u - 1];
        }
        for (int u = 1; u <= n - 2 * i + 1; ++u) {
            int ss = cnts[u + i - 1] - cnts[u - 1], s0 = cnt0[u + i - 1] - cnt0[u - 1];
            if (!s0)
                (res += ps[ss] % mod) %= mod;
        }
    }
    std::cout << res << '\n';
    std::cerr << std::fixed << std::setprecision(6) << std::chrono::duration<double> (std::chrono::steady_clock::now() - stime).count() << "s\n";
    return 0;
}
查看代码

#B. 换乘旅行

http://222.180.160.110:61235/problem/51900

  • 手玩可以发现『环』在题目中是一个很重要的东西
  • 重要性质:考虑一个环第一次被访问的点,一定会从这个点出发把这个环访问一遍,反证法易证。
  • 故可以把每个环给『删除』。可以用下图概括所有情况。

    假设当前以 s 为起点,终点为 t,且之前已经在以 s 为起点时访问过了一部分路径上的点。

    那么此时 mt 是有答案的,而 sm 都可以继承 m 的答案。

#include <bits/stdc++.h>
int main() {
#ifdef ONLINE_JUDGE
    std::freopen("travel.in", "r", stdin);
    std::freopen("travel.out", "w", stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
#else
    std::freopen(".in", "r", stdin);
    std::freopen(".out", "w", stdout);
    const auto stime = std::chrono::steady_clock::now();
#endif
    int n;
    std::cin >> n;
    std::vector<std::vector<int> > g(n + 1);
    std::vector<std::vector<int>::iterator> it(n + 1);
    for (int i = 1, k; i <= n; ++i) {
        std::cin >> k;
        for (int x; k--; )
            std::cin >> x, g[i].push_back(x);
        it[i] = g[i].begin();
    }
    std::vector<int> res(n + 1), inq(n + 1);
    for (int i = 1; i <= n; ++i)
        if (!res[i]) {
            std::stack<int> st;
            for (int x = i; ; ) {
                if (it[x] == g[x].end() || res[x]) {
                    if (!res[x])
                        res[x] = x;
                    res[i] = res[x];
                    for (; !st.empty(); st.pop())
                        res[st.top()] = res[x], inq[st.top()] = 0;
                    break;
                }
                st.push(x), inq[x] = 1, x = *it[x];
                if (inq[x])
                    for (int p = -1; p != x; ) {
                        p = st.top(), st.pop();
                        inq[p] = 0, ++it[p];
                    }
            }
        }
    for (int i = 1; i <= n; ++i)
        std::cout << res[i] << '\n';
#ifndef ONLINE_JUDGE
    std::cerr << std::fixed << std::setprecision(6) << std::chrono::duration<double> (std::chrono::steady_clock::now() - stime).count() << "s\n";
#endif
    return 0;
}
查看代码

言论

若要断酒法,醒眼看醉人。
来自「沪谚」
来发评论吧~
Powered By Valine
v1.5.2