Atcoder Beginner Contest 448

比赛链接:Atcoder Beginner Contest 448

A. chmin

按照题意模拟即可。

时间复杂度: \(O(N)\)。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, x;
    cin >> n >> x;
    for (int i = 0; i < n; i++)
    {
        int a;
        cin >> a;
        if (a < x)
        {
            cout << 1 << "\n";
            x = a;
        }
        else
            cout << 0 << "\n";
    }
    return 0;
}

B. Pepper Addiction

尽量将每一个位置都填充到上限 \(B\),填充完统计最后结果。

时间复杂度: \(O(N)\)。

#include <bits/stdc++.h>
using namespace std;

#define int long long

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, m, ans = 0;
    cin >> n >> m;
    vector<int> c(m);
    for (int i = 0; i < m; i++)
        cin >> c[i];
    for (int i = 1; i <= n; i++)
    {
        int a, b;
        cin >> a >> b;
        int res = min(c[a - 1], b);
        c[a - 1] -= res, ans += res;
    }
    cout << ans << "\n";
    return 0;
}

C. Except and Min

用 multiset 维护当前的数列,每一次查询将要删除的数从 multiset 中删除,输出当前 multiset 中的最小值,再将被删除的数重新插入 multiset 中。

时间复杂度: \(O(N\log N + \sum K \log N)\)

#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 5, K = 10;
int n, q, k, a[N], b[K];

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> q;
    multiset<int> ms;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        ms.insert(a[i]);
    }
    while (q--)
    {
        cin >> k;
        for (int i = 1; i <= k; i++)
        {
            cin >> b[i];
            auto it = ms.find(a[b[i]]);
            if (it != ms.end())
                ms.erase(it);
        }
        cout << *ms.begin() << "\n";
        for (int i = 1; i <= k; i++)
            ms.insert(a[b[i]]);
    }
    return 0;
}

D. Integer-duplicated Path

从节点 \(1\) 开始进行 DFS,利用 unordered_map 记录路径上所有节点的值的出现个数,根据前面路径节点值出现的个数判断当前节点的值是否有出现过。

时间复杂度: \(O(N)\)。

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5;
int n, head[N], a[N], num = 0;
bool ans[N];
struct edge { int to, nxt; } e[N << 1];

void AddEdge(int u, int v)
{
    e[++num] = { v, head[u] };
    head[u] = num;
}

void dfs(int u, int fa, unordered_map<int, int>& vis)
{
    if (ans[fa] || (vis.find(a[u]) != vis.end() && vis[a[u]] > 0))
        ans[u] = true;
    vis[a[u]]++;
    for (int i = head[u]; i; i = e[i].nxt)
    {
        int v = e[i].to;
        if (v == fa)
            continue;
        dfs(v, u, vis);
    }
    vis[a[u]]--;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i], ans[i] = false;
    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        AddEdge(u, v), AddEdge(v, u);
    }
    unordered_map<int, int> vis;
    dfs(1, 0, vis);
    for (int i = 1; i <= n; i++)
    {
        if (ans[i])
            cout << "Yes\n";
        else
            cout << "No\n";
    }
    return 0;
}

E. Simple Division

假设 \(\left \lfloor \frac{N}{M} \right \rfloor = res\) (\(mod\ 10007\)),那么可以得到 \(N = 10007 \times M \times T + M \times res + b\),其中 \(T\) 是一个非负整数,\(b\) 是 \(0\) 到 \(M-1\) 之间的整数。从而得到 \(\left \lfloor \frac{N}{M} \right \rfloor = \left\lfloor \frac{N \% (10007M)}{M} \right\rfloor\)。

根据题目中的规则,可以发现 \(N = N \times 10^l + \frac{10^l - 1}{9} \times c\),但由于模数 \(10007M\) 不是质数,所以对于 \(\frac{10^l - 1}{9}\) 无法利用费马小定理求乘法逆元。

但原式可以变形为 \(9N = 9N \times 10^l + \frac{10^l - 1}{9} \times c\),我们可以先计算 \(9N\),这个时候的模数就变成了 \(10007M \times 9\)。最后要输出的结果是 \(\left\lfloor \frac{N \% (10007M \times 9)}{M} \right\rfloor\)。

时间复杂度: \(O(K\log l)\)。

#include <bits/stdc++.h>
using namespace std;

#define int long long

int qpow(int x, int k, int mod)
{
    int res = 1;
    while (k)
    {
        if (k & 1)
            res = res * x % mod;
        x = x * x % mod;
        k >>= 1;
    }
    return res;
}

int n, m, k;

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> k >> m;
    int mod = 9 * 10007 * m;
    for (int i = 1; i <= k; i++)
    {
        int c, l;
        cin >> c >> l;
        int pw10 = qpow(10, l, mod);
        n = (n * pw10 % mod + (pw10 - 1 + mod) % mod * c % mod) % mod;
    }
    cout << n / m / 9 << "\n";
    return 0;
}

F. Authentic Traveling Salesman Problem

平面范围为 \(X\le 2\times10^7\),点数为 \(N\le6\times10^4\)。如果按普通的 \(x\) 再按 \(y\) 排序,可能会产生大量上下往返,使路径很长。一个常见技巧是 蛇形扫描(Snake Order)。

我们将平面按 \(x\) 坐标分成若干宽度为 \(B\) 的竖条,再进行排序。

排序规则:

  1. 先按每一个块从小到大排序
  2. 若在同一块:
    • 偶数块:按 \(y\) 升序
    • 奇数块:按 \(y\) 降序

这样访问顺序类似:

↑   ↓   ↑   ↓
|   |   |   |
|   |   |   |

路径在相邻块之间会自然连接,减少来回移动。

块大小选择

设块宽为 \(B\),总路径长度可以估计为: \(\frac{X^2}{B} + NB + O(X)\)

当 \(B = \frac{X}{\sqrt N}\) 时,总路径长度最小。

本题中 \(X \le 2e7\), \(N \le 6e4\),因此 \(B \approx 8e4\),可以满足距离限制。

题目要求从 \(1\) 开始出发,因此排序后找到 \(1\) 的位置,再从该位置循环输出即可。

时间复杂度: \(O(N \log N)\)

#include <bits/stdc++.h>
using namespace std;

const int N = 6e4 + 5;
int n, x[N], y[N], p[N];

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    int B = 2e7 / sqrt(N);
    for (int i = 1; i <= n; i++)
        cin >> x[i] >> y[i], p[i] = i;
    sort(p + 1, p + n + 1, [&](int l, int r){
        if (x[l] / B != x[r] / B)
            return x[l] / B < x[r] / B;
        if ((x[l] / B) % 2)
            return y[l] > y[r];
        else
            return y[l] < y[r];
    });
    int pos = 1;
    while (p[pos] != 1)
        pos++;
    for (int i = 0; i < n; i++)
    {
        int idx = pos + i;
        if (idx > n)
            idx -= n;
        cout << p[idx] << " ";
    }
    cout << "\n";
    return 0;
}
posted @ 2026-03-10 21:54  nuo534202  阅读(13)  评论(0)    收藏  举报