代码源周赛 Round 11 题解

A. [R11A]出现奇数次的偶数

我们开一个 map 记录每个数的出现次数。

把数组遍历一遍看一个数如果又是偶数出现次数又是奇数就更新答案,最后输出即可。

预计时间 \(\le 1min\)

B. [R11B]前三小

我们记录二元组 \((x,y)\) 表示第 \(x\) 个数出现位置是 \(y\)。按照 \(x\) 排序后取前三个再按 \(y\) 排序即可。

预计时间 \(\le 2min\)

C. [R11C]匀加速运动

分类讨论是否到达最大速度,开 double 套公式即可,注意 cout<<fixed<<setprecision(n) 可以保留 \(n\) 位小数。

D. [R11D]山谷数

同见:数位 DP 的技巧 的特别情况。

注意到答案很小。我们可以把所有符合条件的数求出来排序,再二分 \(l,r\) 内最小和最大符合条件的数在排序后数组里的位置相减加上一即可。

具体构造方法,先枚举长度,然后枚举最小的数的位置和值,然后朝两边 DFS 回溯决定各个位数即可,注意排除不合法状态。

代码:

#include <bits/stdc++.h>
#define int long long
#define upp(a, x, y) for (int a = x; a <= y; a++)
#define dww(a, x, y) for (int a = x; a >= y; a--)
#define pb(x) push_back(x)
#define endl '\n'
#define x first
#define y second
#define PII pair<int, int>
using namespace std;
vector<int> ans;
map<int, int> ha;
int a[20];
void dfs(int u, int op, int len, int last, int gu) {
    if (!u) {
        int now = 0;
        upp(i, 1, len) {
            now *= 10;
            now += a[i];
        }
        ans.push_back(now);
        return;
    }
    if (!op) {
        upp(i, last + 1, 9 - (len - u + 1) + 1) {
            if (u != len) {
                a[u] = i;
                dfs(u + 1, op, len, i, gu);
            } else {
                if (a[gu] + gu - 1 > i) continue;
                a[u] = i;
                dfs(gu - 1, i, len, a[gu], gu);
            }
        }
    } else {
        upp(i, last + 1, op - u + 1) {
            if (u != 1) {
                a[u] = i;
                dfs(u - 1, op, len, i, gu);
            } else {
                a[u] = op;
                dfs(u - 1, op, len, op, gu);
                break;
            }
        }
    }
}
void init() {
    upp(len, 1, 18) {
        upp(j, 2, len - 1) {
            upp(k, 0, 9) {
                if (k + j - 1 > 9 || k + (len - j) > 9) continue;
                a[j] = k;
                dfs(j + 1, 0, len, k, j);
            }
        }
    }
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    init();
    sort(ans.begin(), ans.end());
    int qq;
    cin >> qq;
    upp(i, 1, qq) {
        int l, r;
        cin >> l >> r;
        cout << upper_bound(ans.begin(), ans.end(), r) -
                    lower_bound(ans.begin(), ans.end(), l)
             << endl;
    }
    return 0;
}

E. [R11E]波浪数

同见:数位 DP 的技巧

  1. 一个数贡献的条件是为波浪数,波浪数说白了就是一上一下,记录这次应该是上还是下或者还未决定 \(go=1/0/2\)。如果还未决定,我们就可以填上继续 \(0\),或者填上一个正数,然后决定上下是 \(0/1\) 各继续计算,注意如果 \(pos=1\) 的时候,上和下都会只取到一个数,为了保证不重不漏,我们只再计算其中一种情况即可。对于之前就已经决定是上还是下,我们直接枚举数就行了,为了保证上下我们记录状态 \(last\) 表示上一个填的数,没什么好讲的。

  2. 所有数的贡献都为 \(1\)

  3. 所有状态为 \(pos,lim,go,last\)

代码:

#include <bits/stdc++.h>
#define int long long
#define upp(a, x, y) for (int a = x; a <= y; a++)
#define dww(a, x, y) for (int a = x; a >= y; a--)
#define pb(x) push_back(x)
#define endl '\n'
#define x first
#define y second
#define PII pair<int, int>
using namespace std;
const int N = 1e5 + 10, X = 998244353;
int f[N][3][10], a[N];
int dfs(int pos, bool lim, int go, int last) { // 0 代表降
    if (f[pos][go][last] != -1 && lim == 0) return f[pos][go][last];
    if (!pos) return 1;
    int res = 0, up = (lim ? a[pos] : 9);
    if (go == 2) {
        upp(i, 0, up) {
            if (i) {
                if (pos > 1)
                    (res += dfs(pos - 1, (lim && i == a[pos]), 1, i)) %= X;
                (res += dfs(pos - 1, (lim && i == a[pos]), 0, i)) %= X;
            } else
                (res += dfs(pos - 1, (lim && i == a[pos]), go, last)) %= X;
        }
    } else if (go == 1) {
        upp(i, last + 1, up) {
            (res += dfs(pos - 1, (lim && i == a[pos]), 0, i)) %= X;
        }
    } else {
        upp(i, 0, min(last - 1, up)) {
            (res += dfs(pos - 1, (lim && i == a[pos]), 1, i)) %= X;
        }
    }
    if (!lim) f[pos][go][last] = res;
    return res;
}
int solve(string x) {
    int len = 0;
    dww(i, x.size() - 1, 0) { a[++len] = x[i] - '0'; }
    return dfs(len, 1, 2, 0);
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    memset(f, -1, sizeof f);
    int qq;
    cin >> qq;
    while (qq--) {
        string l, r;
        cin >> l >> r;
        int ans = ((solve(r) - solve(l)) % X + X) % X;
        int now = 2, flag = 1;
        upp(i, 1, (int)l.size() - 1) {
            if (l[i] == l[i - 1]) {
                flag = 0;
                break;
            }
            if (now == 2) {
                if (l[i] > l[i - 1])
                    now = 0;
                else
                    now = 1;
            } else {
                if (now && l[i] < l[i - 1]) {
                    flag = 0;
                    break;
                }
                if (!now && l[i] > l[i - 1]) {
                    flag = 0;
                    break;
                }
                now ^= 1;
            }
        }
        if (flag || l.size() == 1) ans++;
        cout << ans % X << endl;
    }
    return 0;
}

F. [R11F]二维gcd和 2

参考洛谷 P2303 [SDOI2012] Longge 的问题。这题我们可以直接枚举对答案的贡献 \(\gcd\)

具体来说,就是当两数 \(i,j\) 的最大公约数为 \(x\) 的时候,\(\frac{i}{x}\)\(\frac{j}{x}\) 互质。因此,我们就可以枚举这个 \(x\),统计它对答案贡献了多少次,就可以把原式变成下面这样。

\[\begin{aligned} \large \sum_{k=1}^{n} k\sum_{p=1}^{\lfloor \frac{n}{k} \rfloor} \sum_{q=1}^{\lfloor \frac{n}{k} \rfloor} [\gcd(p,q)=1] \end{aligned} \]

因为 \(i,j\) 必须为 \(k\) 的倍数,所以我们直接枚举是 \(k\) 的几倍,也就是 \(p,q\),这时候 \(\frac{i}{k}=p,\frac{j}{k}=q\)

为了把式子转化成欧拉函数,我们做这样的变换。

\[\begin{aligned} \large \sum_{k=1}^{n} 2k\sum_{p=1}^{\lfloor \frac{n}{k} \rfloor} \sum_{q=1}^{p-1} [\gcd(p,q)=1]+k \end{aligned} \]

这是因为对于对于 \(p,q\) 都从 \(1\) 取到 \(n\) 的时候,一个 \((x,y)(x<y)\) 会被取两次,即 \((p=x,q=y),(p=y,q=x)\)。而 \((x,x)\) 只会被取一次,当 \(x=1\) 时,贡献为 \(1\),否则贡献为 \(0\)

现在变成这样。

\[\begin{aligned} \large \sum_{k=1}^{n} 2k\sum_{p=1}^{\lfloor \frac{n}{k} \rfloor} \varphi(p)-k \end{aligned} \]

很明显,时间复杂度为调和级数,所以这个时间复杂度是 \(O(n\log n)\) 的。

发现我们要查询的 \(\varphi\) 和都是一段,于是我们直接做前缀和,令 \(\displaystyle g(x)=\sum_{i=1}^{x} \varphi(i)\)

答案表示为:

\[\begin{aligned} \large \sum_{k=1}^{n} 2kg(\lfloor \frac{n}{k} \rfloor)-k \end{aligned} \]

直接预处理 \(O(n)\) 即可计算。

其实注意到 \(g(x)\) 函数的自变量是一个分式,所以取值不多,我们可以数论分块做到 \(O(\sqrt{n})\) 解决该问题,不过这题 \(O(n)\) 足矣。

代码:

#include <bits/stdc++.h>
#define upp(a, x, y) for (int a = x; a <= y; a++)
#define dww(a, x, y) for (int a = x; a >= y; a--)
#define pb(x) push_back(x)
#define endl '\n'
#define x first
#define y second
#define PII pair<int, int>
using namespace std;
const int EU = 5e7 + 10, X = 998244353;
int st[EU], phi[EU], sumphi[EU];
int prs[EU],top;
void init(int n) {
    phi[1] = 1;
    upp(i, 2, n) {
        if (!st[i]) {
            prs[top++]=i;
            phi[i] = i - 1;
        }
        for (int j = 0; prs[j] <= n / i; j++) {
            st[i * prs[j]] = 1;
            if (i % prs[j] == 0) {
                phi[i * prs[j]] = prs[j] * phi[i];
                break;
            } else
                phi[i * prs[j]] = (prs[j] - 1) * phi[i];
        }
    }
    upp(i, 1, n) sumphi[i] = (sumphi[i - 1] + phi[i]) % X;
    return;
}
int n, ans;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n;
    init(n);
    upp(k, 1, n) {
        ans = ((ans + (2 * (long long)k * sumphi[n / k]) % X - k) % X + X) % X;
    }
    cout << ans << endl;
    return 0;
}
posted @ 2025-05-13 21:57  PM_pro  阅读(127)  评论(0)    收藏  举报