竞赛讨论区 > 小白月赛84出题人题解
头像
cbyyx
编辑于 2023-12-22 22:02
+ 关注

小白月赛84出题人题解

update: E题python选手读入可能会出问题,现已对赛时提交返回错误代码重测,由于出题人不会python,对于赛时这个问题无法解决,对此深感抱歉orz。

对于 题由于数据问题导致了极少数人本应返回答案错误却返回答案正确,对此出题人深表抱歉,数据已更新,给大家带来的不便敬请谅解。

A.打靶

判断一下是否 即可,注意判断

复杂度

#include<bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        long long n, m, X, Y;
        cin >> n >> m >> X >> Y;
        if (n - m >= Y - X && Y >= X)cout << "Yes\n";
        else cout << "No\n";
    }
}

B.小蓝的疑惑

第一种解法(常规解法):

前置知识:对于任意两个正整数 都有

,对 进行因数分解,可以找出所有的 使得

对所有的 判断是否满足 即可。

复杂度

#include<bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        long long x, y;
        cin >> x >> y;
        long long res = x * y;
        bool f = 0;
        for (long long i = 1; i * i <= res; i++) {
            if (res % i == 0) {
                long long j = res / i;
                if (gcd(i, j) == x && lcm(i, j) == y) {
                    cout << i << ' ' << j << '\n';
                    f = 1;
                    break;
                }
            }
        }
        if (!f)cout << -1 << '\n';
    }
}

第二种解法:

前置知识:对于任意两个正整数 都有

由于上述条件在,只需要判断一下给定的 是否符合 的条件,若满足直接输出 即为答案,否则输出

复杂度

#include<bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        long long x, y;
        cin >> x >> y;
        if (y % x == 0)cout << x << ' ' << y << '\n';
        else cout << -1 << '\n';
    }
}

以上两种解法复杂度均能通过。

C.𝑘级序列

假设 ,对于 ,设 ,判断是否 即可,如果满足则令 ,否则输出

复杂度

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long a[N];
long long b[N];
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n, k;
        cin >> n >> k;
        b[0] = -0x3f3f3f3f3f3f3f3f;
        bool f = 1;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            long long c = a[i] - k, d = a[i] + k;
            if (d >= b[i - 1])b[i] = max(b[i - 1], c);
            else f = 0;
        }
        if (!f)cout << "No\n";
        else cout << "Yes\n";
    }
}

D.Reverse

可以发现翻转的区间有一部分贡献的答案和翻转前贡献的答案一样。

已知每次询问独立,对于每次询问,快速查询区间 中有多少段连续的

为第 个字符前面(包括第 个字符)中最近的 出现的位置。

为第 个字符后面(包括第 个字符)中最近的 出现的位置。

,则更新

,则更新

然后对结果加上区间 中贡献的答案即可。

注意一些代码上的细节。

其中快速查询区间的贡献可以通过前缀和预处理做出。

复杂度

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int pre[N], suf[N];
int dp[N][2];
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, q;
    cin >> n >> q;
    string s;
    cin >> s;
    s = "0" + s + "0";
    int last = 0;
    for (int i = 1; i <= n; i++) {
        if (s[i] == '0')last = i;
        pre[i] = last;
        dp[i][0] = dp[i - 1][0];
        if (s[i] == '1' && s[i - 1] == '0')dp[i][0]++;
    }
    dp[n + 1][1] = 0;
    last = n + 1;
    for (int i = n; i >= 0; i--) {
        if (s[i] == '0')last = i;
        suf[i] = last;
        dp[i][1] = dp[i + 1][1];
        if (s[i] == '1' && s[i + 1] == '0')dp[i][1]++;
    }
    while (q--) {
        int l, r;
        cin >> l >> r;
        int L = l, R = r;
        int res = dp[l - 1][0] + dp[r + 1][1];
        if (s[l - 1] == '1')R = pre[R];
        if (s[r + 1] == '1')L = suf[L];
        if (R >= L) {
            if (s[L] == '0')res += dp[R][0] - dp[L - 1][0];
            else res += dp[L][1] - dp[R + 1][1];
        }
        if (s[l - 1] == '1' && s[r + 1] == '1' && R < L)res--;
        cout << res << '\n';
    }
}

E.Dog vs Cat

如果序列长度为 ,那么看操作次数即可判断谁胜谁负。

如果序列长度为 ,可以发现:

如果序列是形如 的形式,那么第一个操作的人为了不败,只能选择第二个元素进行操作,此时若 ,则第一个人输掉了比赛,否则序列将会变成 的形式,此时第二个人必败。

否则第一个人必败。

如果序列长度大于 ,则仅有当 才会分出胜负,否则比赛将一直进行。

证明:若当 时就分出胜负,我们不妨回到最后一步,此时行动的人如果选择序列中的某一个 进行操作,则 更新为 ,且 ,已知区间长度大于 ,因此此时行动的 人并不会被判负,因此当 时就分出胜负是不可能的。

由上述可知,最终 的个数为 ,其余元素全为 时为最终状态,计算总行动次数,判断奇偶即可获得答案。

复杂度

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)cin >> a[i];
        if (n == 1) {
            if (a[1] == 0 || a[1] & 1)cout << "Dog\n";
            else cout << "Cat\n";
        } else if (n == 2) {
            if (a[1] > a[2])swap(a[1], a[2]);
            if (a[2] == 0)cout << "Dog\n";
            else {
                if (a[2] == a[1] + 1) {
                    if (a[1] == 0)cout << "Dog\n";
                    else cout << "Cat\n";
                } else cout << "Dog\n";
            }
        } else {
            int i = (n - 1) / 2;
            int j = n - i;
            long long res = 0;
            int cnt0 = 0;
            for (int i = 1; i <= n; i++)res += a[i], cnt0 += (a[i] == 0);
            if (res == 0 || n - cnt0 <= cnt0)cout << "Dog\n";
            else {
                res -= j;
                if (res & 1)cout << "Cat\n";
                else cout << "Dog\n";
            }
        }
    }
}

F.小蓝的构造

对于本题,首先我们可以知道,当且仅当 时, ,否 则, ,那么,当题目中给定的 时,我们可以直接构造出 ,并且认为 ,综上所述,我们可以找到一个新的 使得 ,此时则 必有 && 才能使得题目有解 ,于是我们可以知道,两端端点,即此时的 的值是定的。

假设现在 表示当前构造出的串中 ~

接着考虑从第 个位置转移到第 个位置造成的影响(即从两端往中间构 造),当 并且 之后,将不会对 造成贡献,并且由于串 的两端一定是 ,所以我们不管第 个位置填什么,最终只会使得 的值增加不超过 。于是我们可以考虑从两端向里的过程中维护 ~ ,当我们左端点更新为 时, 则一定无解。

接下来考虑三种情况

,此时第 个位置一定为 ,第 个位置一定为

, 此时第 个位置要么都是 ,要么都是 。两种情况分别维护即可

, 此时第 个位置一定为 ,第 𝑟 个位置一定为

综上递归维护即可,最终时间复杂度

#include<bits/stdc++.h>
using namespace std;
int f[50];//表示f(t,1)~f(t,n-1)
int a[50];//表示dp(1)~dp(n-1)
int res[50];
int ans[50];//最终输出的答案
int n;
void updatel(int l, int d, int v,
             int op) {//更新第[1,d-1]和第 l 位产生的贡献
    for (int i = 1; i < d; i++) {
        if (res[i] == v)a[l - i] += op;
    }
}
void updater(int r, int d, int v,
             int op) {//更新第[d+1,n]和第 r 位产生的贡献
    for (int i = d + 1; i <= n; i++) {
        if (res[i] == v)a[i - r] += op;
    }
}
void dfs(int l, int r) {
    for (int i = 1; i <= n; i++) {
        if (a[i] > f[i])return ;
    }
    if (l > r) {
        for (int i = 1; i < n; i++) {
            if (a[i] != f[i])return;
        }//不合法
        for (int i = 1; i <= n; i++)ans[i] = res[i]; //更新答案
        return;
    }
    if (l == r) { //若L=R,暴力判断此时第L个位置填1还是0即可
        res[l] = 1;
        updatel(l, l, 0, 1);
        dfs(l + 1, r - 1);
        updatel(l, l, 0, -1);
        res[l] = 0;
        updater(l, l, 1, 1);
        dfs(l + 1, r - 1);
        updater(l, l, 1, -1);
        return;
    }

    if (l == 1) {
        res[l] = 0, res[r] = 1;
        a[n - l] = 1;
        dfs(l + 1, r - 1);
    } else {
        int len = n - l;
        if (a[len] + 2 < f[len])return;

        if (a[len] == f[len]) {
            res[l] = 1, res[r] = 0;
            updatel(l, l, 0, 1);
            updater(r, r, 1, 1);
            dfs(l + 1, r - 1);
            updatel(l, l, 0, -1);
            updater(r, r, 1, -1);
        } else if (a[len] + 1 == f[len]) {
            res[l] = res[r] = 1;
            updatel(l, l, 0, 1);
            updatel(r, l, 0, 1);
            dfs(l + 1, r - 1);
            updatel(l, l, 0, -1);
            updatel(r, l, 0, -1);
            res[l] = res[r] = 0;
            updater(l, r, 1, 1);
            updater(r, r, 1, 1);
            dfs(l + 1, r - 1);
            updater(l, r, 1, -1);
            updater(r, r, 1, -1);
        } else {
            res[l] = 0, res[r] = 1;
            updatel(r, l, 0, 1);
            updater(l, r, 1, 1);
            a[r - l]++;
            dfs(l + 1, r - 1);
            updatel(r, l, 0, -1);
            updater(l, r, 1, -1);
            a[r - l]--;
        }
    }
}
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for (int i = 1; i < n; i++)cin >> f[i];
    memset(ans, -1, sizeof ans);
    int m = n;
    while (n > 1 && f[n - 1] == 0)ans[n] = 0, n--;
    dfs(1, n);
    if (ans[1] != -1) {
        for (int i = 1; i <= m; i++)cout << ans[i];
        cout << '\n';
    } else cout << -1 << '\n';
}

全部评论

(1) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐