AtCoder Beginner Contest 245

比赛相关信息

比赛信息

比赛名称: AtCoder Beginner Contest 245
比赛地址: Atcoder

比赛过程回顾

A B C D E F G
提交次数 1 1 1 1 0 0 0
首次提交时间 00:07:10 00:08:41 00:27:39 00:53:34
首A时间 00:07:10 00:08:41 00:27:39 00:53:34

部分题解与小结

C - Choose Elements

小评

刚拿到这题以为是一道很难的搜索甚至是 \(\tt{}DP\) 题,想了很久,后来才发现使用贪心策略就可以搞定。

题意

给出两个长度均为 \(N\) 的序列 \(a,b\) ,询问是否存在一个长度为 \(N\) 的序列 \(x\) ,使得满足:

  • 对于任意 \(i\) ,都有 \(x_i=a_i\)\(x_i=b_i\)
  • \(|x_{i}-x_{i+1}|\le K\)

思路

赛时思路(正解)

\(1\)\(N\) 遍历 \(a,b\) 数组,使用两个标记记录 \(a_i,b_i\) 能否被保留。

AC代码

点击查看代码
//====================
#define int LL
const int N    = 1e6 + 7;
int a[N], b[N];
//====================
void Solve() {
    int n, k; cin >> n >> k;
    FOR (i, 1, n) cin >> a[i];
    FOR (i, 1, n) cin >> b[i];
    int prea = 1, preb = 1;
    FOR (i, 2, n) {
        int fa = 0, fb = 0;
        if (prea == 1) {
            if (abs(a[i - 1] - a[i]) <= k) fa = 1;
            if (abs(a[i - 1] - b[i]) <= k) fb = 1;
        }
        if (preb == 1){
            if (abs(b[i - 1] - a[i]) <= k) fa = 1;
            if (abs(b[i - 1] - b[i]) <= k) fb = 1;
        }
        if (fa == 0 && fb == 0) {
            cout << "No";
            return;
        }
        prea = fa, preb = fb;
    }
    cout << "Yes";
}

D - Polynomial division

小评

这题刚拿到手的时候一脸懵,以为用到了什么高难的数论。说来很妙,我通过机械寻找规律之后发现有递推公式,于是很快的切掉了。

题意

有一个长度为 \(N\) 的多项式 \(A\) ,一个长度为 \(M\) 的多项式 \(B\) ,两者相乘得到多项式 \(C\) 。现在给出 \(A,C\) 的各个系数,求解 \(B\) 的各个系数。保证最大项的系数不为 \(0\) ,系数均为整数。

思路

赛时思路(正解)

推导如下:

我们正向考虑整个过程,设 \(A\) 序列各系数 \(A_3,A_2,A_1,A_0\)\(B\) 序列各系数 \(B_5,B_4,…,B_0\)

\[\left\{\begin{matrix} C_8= & A_3*B_5 && B_5 ✔ \\ C_7= & A_3*B_4+A_2*B_5 && B_4✔ \\ C_6= & A_3*B_3+A_2*B_4+A_1*B_5 && B_3✔ \\ …… & …… && …… \end{matrix}\right.\]

随后,我们容易逆向推导出 \(B\) 序列的各项系数,赛时推导代码如下:

// b[m]     = (c[n + m]                                                                  ) / a[n];
// b[m - 1] = (c[n + m - 1] - a[n - 1] * b[m]                                            ) / a[n];
// b[m - 2] = (c[n + m - 2] - a[n - 2] * b[m] - a[n - 1] * b[m - 1]                      ) / a[n];
// b[m - 3] = (c[n + m - 3] - a[n - 3] * b[m] - a[n - 2] * b[m - 1] - a[n - 1] * b[m - 2]) / a[n];

至此,只需要使用循环的方式复现上述的逆向推导过程即可。

AC代码

点击查看代码
//====================
const int N = 1010;
int a[N], b[N], c[N];
//====================
void Solve() {
    int n, m; cin >> n >> m;
    FOR (i, 0, n) cin >> a[i];
    FOR (i, 0, n + m) cin >> c[i];
    FOR (i, 0, m) {
        int num = c[n + m - i];
        FOR (j, 0, i - 1) num -= a[n - i + j] * b[m - j];
        b[m - i] = num / a[n];
    }
    FOR (i, 0, m) cout << b[i] << " ";
}

F - Endless Walk

小评

初见杀,赛时以为这是道高阶图论题,于是上网搜板子,但是都不太对。赛后看别人代码才意识到这是一道入门的不能再入门的图论,唯一的难点在于反向建图,%%%。

题意

给定一张有向有环图,询问有多少点可以作为起点,到达一个环。

思路

赛时思路(是错的)
点击查看原因

基于自己有限的认知,复盘一下赛时的思路。

首先我读到这道题需要求解的是环上点的数量以及有多少环外的点可以到达环内,于是我便有了“ \(\tt{}DFS\) 搜环内 + \(\tt{}DSU\) 搜环外”的主体思路,我飞速的上网找了 \(\tt{}DFS\) 的代码(顺利的完成了找环内点的工作),然后在其基础上修改,随后我发现问题在于 \(\tt{}DSU\) ,如果加上了路径压缩就无法复盘找到路径上的点。基于搜索到的代码,我又有了利用 \(\tt{}DFS\) 递归时可以记录父节点的这一特性进行思考(类似于染色的思路,染完环内后回溯染色路径)。

最终在赛时还是没能成功解出这道题,在赛后补题时又尝试复原此思路,成功的通过了大部分测试点,但是被其中一个卡了……然后恰巧同一时间我也遇到了另外一道卡 \(\tt{}DFS\) 染色的题目,推测原因相同,都是因为递归层数过多导致函数栈溢出,但是具体原因未做深究。

正解

由于本题是有向边,我们应当要有反向建图、思考的直觉。

题中需要寻找直接到达环的点,即为总点数 \(N\) 减去到达不了环内的点,我们如果能求解出到达不了环内的点那么一样可以解出这道题。思考反向建图,每次移除那些入度为 \(0\) 的点即为永远无法到达环内的点。即解本题。

AC代码

点击查看代码
赛时思路
//====================
#define int LL
const int N = 1e6 + 7;
//====================
LL tot, h[N], ver[N], ne[N];
void add(int x, int y) {
    ver[++ tot] = y, ne[tot] = h[x], h[x] = tot;
}
int v[N], fa[N], num, color[N];
void dfs(int x) {
    v[x] = 1;
    for (int i = h[x]; i; i = ne[i]) {
        int y = ver[i];
        if (v[y] == 0) { //如果未被搜索过
            fa[y] = x; //记录父亲节点
            dfs(y);
        }else if (v[y] == 1) { //如果已经被搜索过,染环内
            int pre = x;
            while (pre != y) { //回溯,直到找到环的开头
                color[pre] = 1;
                pre = fa[pre];
            }
            color[pre] = 1;
        }
        if (color[y] == 1) color[x] = 1; //判断是否要回溯染路径
    }
    v[x] = 2;
}

void Solve() {
    int n, m; cin >> n >> m;
    FOR (i, 1, m) {
        int x, y; cin >> x >> y;
        add(x, y);
    }
    FOR (i, 1, n) {
        if (v[i] == 0) dfs(i);
    }
    FOR (i, 1, n) {
        if (color[i] == 1) ++ num;
    }
    cout << num << endl;
}
正解
//====================
#define int LL
const int N = 1e6 + 7;
//====================
int n, m; 
LL tot, h[N], ver[N], ne[N];
LL deg[N], num;
void add(int x, int y) {
    ver[++ tot] = y, ne[tot] = h[x], h[x] = tot;
    ++ deg[y];
}
void bfs() {
    queue<int> q;
    FOR (i, 1, n) {
        if (deg[i] == 0) q.push(i);
    }
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = h[x]; i; i = ne[i]) {
            int y = ver[i];
            -- deg[y];
            if (deg[y] == 0) q.push(y);
        }
        ++ num;
    }
}
void Solve() {
    cin >> n >> m;
    FOR (i, 1, m) {
        int x, y; cin >> x >> y;
        add(y, x);
    }
    bfs();
    cout << n - num << endl;
}

文 / WIDA
2022.04.04 成文
首发于WIDA个人博客,仅供学习讨论


posted @ 2022-04-04 16:39  hh2048  阅读(116)  评论(0)    收藏  举报