题解 | 周赛 Round 140

A 小红的区间计数

知识点:计数

三个数字分别判断一下是否在 $[l,r]$ 的区间内,是则 $-1$​ ,注意判断这三个数字是否有重复。

时间复杂度 $\mathcal{O}(1)$

void solve() {
    int l, r;
    vector<int> a(3);
    cin >> a[0] >> a[1] >> a[2] >> l >> r;
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
    auto In = [&](int x, int l, int r) { return l <= x && x <= r; };
    int ans = r - l + 1;
    for (auto v : a) ans -= In(v, l, r);
    cout << ans << endl;
}

B 小红的牛魔

知识点:栈

本质括号匹配,用栈模拟一下,每次判断一下栈顶的几个元素是否可删即可,这里为了实现方便使用 $\text{string}$ 来实现栈。

时间复杂度 $\mathcal{O}(n)$

void solve() {
    int n;
    string s;
    cin >> n >> s;
    string st = "";
    for (auto v : s) {
        st += v;
        if (st.size() >= 3) {
            if (st.substr(st.size() - 3) == "niu") st.pop_back(), st.pop_back(), st.pop_back();
        }
        if (st.size() >= 2) {
            if (st.substr(st.size() - 2) == "mo") st.pop_back(), st.pop_back();
        }
    }
    cout << (st.empty() ? "Yes" : "No") << endl;
}

C 小红的矩阵计数

知识点:枚举

不妨把 $\text{L}$ 块补全为 $2\times 2$​ 的正方形,用相对于左上格子的相对位移表示 $L$ 块,枚举左上格子计数即可。

时间复杂度 $\mathcal{O}(nm)$

void solve() {
    int n, m;
    cin >> n >> m;
    vector<string> g(n);
    for (int i = 0; i < n; ++i) cin >> g[i];
    auto check = [&](int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; };
    auto calc = [&](vector<PII> sp) {
        int res = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                unordered_set<int> st;
                for (auto [u, v] : sp) {
                    int nx = i + u, ny = j + v;
                    if (!check(nx, ny)) {
                        break;
                    }
                    st.insert(g[nx][ny] - '0');
                }
                if (st.size() == 3) res++;
            }
        }
        return res;
    };
    cout << calc({{0, 1}, {1, 1}, {1, 0}}) + calc({{0, 0}, {0, 1}, {1, 1}}) + calc({{1, 1}, {1, 0}, {0, 0}}) + calc({{1, 0}, {0, 0}, {0, 1}}) << endl;
}

D/E 小红的排序(hard)

知识点:图论,并查集

对于每一组交换,必然有 $(a,b)(b,c)\rightarrow (a,c)$ 。那么我们对所有可交换的元素连边,连通块内的元素显然两两可达。最后检查 $a[i]$ 和 $i$ 是否在同一个连通块内即可,使用并查集查询即可。

时间复杂度 $\mathcal{O}(n)$

void solve() {
    int n, x, y;
    cin >> n >> x >> y;
    vector<int> p(n + 1), pos(n + 1);
    for (int i = 1; i <= n; ++i) cin >> p[i], pos[p[i]] = i;
    DSU dsu(n);
    for (int i = 1; i <= n; ++i) {
        if (i + x <= n) dsu.merge(i, i + x);
        if (i + y <= n) dsu.merge(i, i + y);
    }
    for (int i = 1; i <= n; ++i) {
        if (!dsu.same(i, pos[i])) {
            cout << "No" << endl;
            return;
        }
    }
    cout << "Yes" << endl;
}

F 小红的三角形构造

知识点:数学,构造

对于一个直角三角形,我们可以用
$$\begin{cases}a = n^2-m^2\\b = 2nm\\c = n^2+m^2\end{cases}$$
进行描述。

所以:

  • 如果 $x$ 是奇数,我们令 $a = x = n^2-m^2 = (n+m)(n-m)$ ,取 $n-m = 1, n+m=x$ ,解得
    $$n = \frac{x+1}{2}, m = \frac{x-1}{2}$$
    计算出 $b,c$ 即可。
  • 如果 $x$ 是偶数,我们令 $b=x=2mn$ ,取 $n=\frac{x}{2},m=1$ ,计算 $a,c$​ 即可。

时间复杂度 $\mathcal{O}(1)$

void solve() {
    int x;
    cin >> x;
    if (x == 1 || x == 2)
        cout << "No" << endl;
    else if (x & 1) {
        int n = (x + 1) / 2, m = (x - 1) / 2;
        cout << "Yes" << endl;
        cout << n * n - m * m << " " << 2 * n * m << " " << n * n + m * m << endl;
    } else {
        int n = x / 2, m = 1;
        cout << "Yes" << endl;
        cout << n * n - m * m << " " << 2 * n * m << " " << n * n + m * m << endl;
    }
}

G 小红的生成树构造

知识点:生成树,构造,连通块,并查集

我们可以将题目的条件转化为:若我们将所有点分为两类:$S1={A,B}$ 和 $S2={C,D}$ ,则在这条连接 $A$ 和 $B$ 的路径上,所有经过的节点都必须属于

$S1$​ ,也就是说,$A$ 和 $B$ 的匹配必须完全在 $S1$ 的同色连通块内部发生;$C$ 和 $D$ 的匹配必须完全在 $S2$​ 的同色连通块内部发生。

所以,在任何合法的生成树中,去除连接 $S1$ 和 $S2$ 之间的所有边后,我们会得到一个只由 $S1$ 节点构成与只由 $S2$ 节点构成的森林。对于每棵处于 $S1$ 中的树,它要么为空,要么必须同时包含至少一个 $A$ 和至少一个 $B$ 。因为只有这样,树中的 $A$ 才能通向 $B$ ,且路径仅由 $S1$ 内节点构成。同理,每棵处于 $S2$ 中的树也必须同时包含至少一个 $C$ 和至少一个 $D$​ 。

所以我们先连所有同组边,检查每一个连通块内是否包含该组的每组元素至少一个,然后将不同组的连通块之间相连即可。

时间复杂度 $\mathcal{O}(m)$

struct DSU {
    vector<int> f, siz;
    vector<int> mask;

    DSU() {}
    DSU(int n) {
        init(n);
    }

    void init(int n) {
        f.resize(n + 1);
        mask.resize(n + 1);
        iota(f.begin(), f.end(), 0);
        siz.assign(n + 1, 1);
    }

    int find(int x) {
        return f[x] == x ? x : f[x] = find(f[x]);
    }

    void merge(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) return;
        mask[x] |= mask[y];
        f[y] = x;
        siz[x] += siz[y];
    }

    bool same(int x, int y) {
        return find(x) == find(y);
    }

    int size(int x) {
        return siz[find(x)];
    }
};

void init() {}

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

    vector<int> type(n + 1);
    DSU dsu(n);

    for (int i = 0; i < n; ++i) {
        if (s[i] == 'A') {
            type[i + 1] = 0, dsu.mask[i + 1] = 1;
        } else if (s[i] == 'B') {
            type[i + 1] = 0, dsu.mask[i + 1] = 2;
        } else if (s[i] == 'C') {
            type[i + 1] = 1, dsu.mask[i + 1] = 4;
        } else if (s[i] == 'D') {
            type[i + 1] = 1, dsu.mask[i + 1] = 8;
        }
    }
    vector<PII> edges(m), ans;
    for (int i = 0; i < m; ++i) cin >> edges[i].fi >> edges[i].se;

    for (auto [u, v] : edges) {
        if (type[u] == type[v]) {
            if (!dsu.same(u, v)) {
                dsu.merge(u, v);
                ans.pb({u, v});
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        if (dsu.f[i] == i) {
            if (type[i] == 0) {
                if ((dsu.mask[i] & 1) == 0 || (dsu.mask[i] & 2) == 0) {
                    cout << "No" << endl;
                    return;
                }
            } else {
                if ((dsu.mask[i] & 4) == 0 || (dsu.mask[i] & 8) == 0) {
                    cout << "No" << endl;
                    return;
                }
            }
        }
    }

    for (auto [u, v] : edges) {
        if (type[u] != type[v]) {
            if (!dsu.same(u, v)) {
                dsu.merge(u, v);
                ans.pb({u, v});
            }
        }
    }

    if (ans.size() != n - 1) {
        cout << "No" << endl;
        return;
    }

    cout << "Yes" << endl;
    for (auto [u, v] : ans) {
        cout << u << " " << v << endl;
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇