题解 | 小白月赛 Round 138

A Sleeping Time

知识点:模拟、取模

时刻每经过 $24$ 小时循环一次,因此起床时刻就是 $(x+t)\bmod 24$。

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

void solve() {
    int x, t;
    cin >> x >> t;
    cout << (x + t) % 24 << endl;
}

B Is it Palidrome?

知识点:字符串、回文、分类讨论

回文只要求每一对对称位置相同,各对位置之间互不影响。

枚举所有对称位置:

  • 若存在两个不同的确定字母,无论如何替换都不能回文,输出 $\texttt{impossible}$;
  • 否则一定能补成回文,此时只要某一对中存在问号,也能把这一对填成不同字母,输出 $\texttt{possible}$;若所有对称位置都是相同的确定字母,则输出 $\texttt{certainly}$。

奇数长度时,中间字符不影响回文性,即使它是问号也不用考虑。

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

void solve() {
    int n;
    string s;
    cin >> n >> s;
    bool f = 0;
    for (int i = 0; i < n / 2; ++i) {
        if (s[i] == s[n - i - 1]) {
            if (s[i] == '?') f = 1;
        } else if (s[i] == '?' || s[n - i - 1] == '?') {
            f = 1;
        } else {
            return cout << "impossible" << endl, void();
        }
    }
    cout << (f ? "possible" : "certainly") << endl;
}

C AND and OR

知识点:位运算、数学、哈希表

对于每一个二进制位,两个数在这一位上的和,都等于按位与、按位或在这一位上的和,因此

$$(x\mathbin{\&}y)+(x\mathbin{|}y)=x+y.$$

问题转化为寻找 $a_i+a_j=k$。从左到右枚举当前数 $x$,用哈希表记录之前出现的数及其下标;若 $k-x$ 已经出现,就找到了一组答案。先查询再插入,保证两个下标不同且前者小于后者。若始终未找到,输出 $-1$。

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

void solve() {
    int n, k;
    cin >> n >> k;
    unordered_map<int, int> mp;
    bool f = 0;
    int a, b;
    for (int i = 1, x; i <= n; ++i) {
        cin >> x;
        if (!f && mp.find(k - x) != mp.end()) a = mp[k - x], b = i, f = 1;
        mp[x] = i;
    }
    if (!f) return cout << -1 << endl, void();
    cout << a << " " << b << endl;
}

D Light up the Graph

知识点:图论、贡献拆分、贪心

点亮一个点,只会给它的每个白色邻点带来 $+1$ 的贡献、每个黑色邻点带来 $-1$ 的贡献。设 $a_u$ 为点 $u$ 的白色邻点数减去黑色邻点数,$c_u$ 表示是否点亮,则图的总价值为

$$\sum_{u=1}^{n}c_u a_u.$$

每个 $c_u$ 都可以独立选择,因此点亮所有 $a_u>0$ 的点即可;$a_u=0$ 的点不影响答案,可以不点亮。枚举每条边,用另一端的颜色更新两端的贡献。

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

void solve() {
    int n, m;
    string s;
    cin >> n >> m >> s;
    vector<int> a(n);
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        a[u] += s[v] == '0' ? 1 : -1;
        a[v] += s[u] == '0' ? 1 : -1;
    }

    vector<int> ans;
    for (int i = 0; i < n; ++i)
        if (a[i] > 0) ans.push_back(i + 1);
    cout << ans.size() << endl;
    for (auto v : ans) cout << v << " ";
    cout << endl;
}

E Rock-Paper-Scissors String Game

知识点:博弈、构造、分类讨论

有合法操作就先手必胜,因为总能一步删到无法操作的状态。

记 $x\succ y$ 表示字符 $x$ 能赢过字符 $y$。若首字符能赢尾字符,直接删掉整个串。否则,设首尾字符为 $x,y$,把开头连续的 $x$ 和结尾连续的 $y$ 留在外面,中间记为 $[l,r]$。既然存在合法操作,中间一定非空。

  • 若 $s_l\succ y$,删除后缀 $[l,n]$,只留下 $x$。
  • 若 $x\succ s_r$,删除前缀 $[1,r]$,只留下 $y$。
  • 否则,由 $s_l\ne x,s_r\ne y$ 和循环克制关系,必有 $s_l\succ s_r$,删除 $[l,r]$。

剩余串至多是前面一段 $x$、后面一段 $y$,而 $x$ 不能赢过 $y$,所以后手无法操作。

因此,从左到右记录出现过的字符,只要有某个已出现的字符能赢当前字符,就输出 $\texttt{Alice}$;否则输出 $\texttt{Bob}$。

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

void solve() {
    int n;
    string s;
    cin >> n >> s;
    unordered_map<char, bool> vis;
    for (auto c : s) {
        if ((c == 'R' && vis.find('P') != vis.end()) ||
            (c == 'P' && vis.find('S') != vis.end()) ||
            (c == 'S' && vis.find('R') != vis.end()))
            return cout << "Alice" << endl, void();
        vis[c] = 1;
    }
    cout << "Bob" << endl;
}

F Permutation of RBS

知识点:括号树、组合计数、栈、乘法逆元

把每个括号对看作一个结点,父亲是直接包含它的括号对,便得到一片森林。题目要求祖先的权值小于后代,等价于每个父亲的权值小于儿子。设 $siz_u$ 为以 $u$ 为根的子树大小,则答案为

$$\frac{n!}{\displaystyle\prod_{u=1}^{n}siz_u}.$$

考虑一棵子树:根必须取分配给这棵子树的最小权值,剩余权值按各儿子的子树大小分组,再分别递归计数。设 $f_u$ 表示给定一组不同权值时,这棵子树的合法赋值数,则

$$f_u=\frac{(siz_u-1)!}{\displaystyle\prod_{v\text{ 是 }u\text{ 的儿子}}siz_v!}\prod_{v\text{ 是 }u\text{ 的儿子}}f_v.$$

给所有树根接一个权值固定为 $0$ 的虚根,把转移展开:每个真实结点留下因子 $(siz_u-1)!/siz_u!=1/siz_u$,虚根贡献 $n!$,就得到上面的公式。

用栈匹配括号即可求出子树大小:读到左括号时创建大小为 $1$ 的结点并入栈;读到右括号时,这个结点的子树已经统计完毕,将其大小累加到父亲,并让答案除以它的大小。

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

constexpr int mod = 1E9 + 7;
using Z = MInt<mod>;

void solve() {
    int n;
    string s;
    cin >> n >> s;
    vector<int> siz(n), st;
    Z ans = 1;
    for (int i = 1; i <= n; ++i) ans *= i;

    int id = 0;
    for (auto c : s) {
        if (c == '(') {
            siz[id] = 1;
            st.push_back(id++);
        } else {
            int u = st.back();
            st.pop_back();
            ans /= siz[u];
            if (!st.empty()) siz[st.back()] += siz[u];
        }
    }

    cout << ans << endl;
}

约定

#include <bits/stdc++.h>
using namespace std;
 
#define int long long
#define pii array<int, 2>
#define endl "\n"
 
void solve() {
     
}
 
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout << fixed << setprecision(20);
    int t = 1;
    cin >> t;
    for (int i = 0; i < t; ++i) {
        solve();
    }
    return 0;
}

使用到的算法模板见 $\texttt{github}$ 仓库 [林月的XCPC模板库](https://github.com/Anoth3rr/XCPC)

暂无评论

发送评论 编辑评论


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