2024 ICPC ShaanXi Provincial Contest

A. chmod

模拟

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {
    string s;
    cin >> s;
    int a = s[0] - '0', b = s[1] - '0', c = s[2] - '0';
    if (a & 4) cout << "r";
    else cout << "-";
    if (a & 2) cout << "w";
    else cout << "-";
    if (a & 1) cout << "x";
    else cout << "-";

    if (b & 4) cout << "r";
    else cout << "-";
    if (b & 2) cout << "w";
    else cout << "-";
    if (b & 1) cout << "x";
    else cout << "-";

    if (c & 4) cout << "r";
    else cout << "-";
    if (c & 2) cout << "w";
    else cout << "-";
    if (c & 1) cout << "x";
    else cout << "-";

    cout << "\n";
}

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();

    return 0;
}

B. Expression Matrix

搜索,首先要尽可能多的使用符号,所以可以dp 出放置符合最多的方案。

然后搜索枚举每一个符号是加还是乘。

考虑一个剪枝优化,只有当符号所在的行或列存在两个11的时候才能使用加,否则只能使用乘

#include <bits/stdc++.h>

using namespace std;

using i32 = int;
using i64 = long long;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;
const int INF = LLONG_MAX / 2;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector f(n + 1, vi(m + 1));
    int T = 0;
    vector<pii> opt;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (i == 1 or j == 1 or i == n or j == m) f[i][j] = 1;
            else if (f[i - 1][j] == 1 and f[i][j - 1] == 1) f[i][j] = -(T++), opt.emplace_back(i, j);
            else f[i][j] = 1;
        }

    vector<vi> r(n + 1);
    for (int i = 1, pre; i <= n; i++) {
        pre = 0;
        for (int j = 1; j <= m; j++) {
            if (f[i][j] == 1) pre = pre * 10 + 1;
            else r[i].push_back(pre), r[i].push_back(f[i][j]), pre = 0;
        }
        r[i].push_back(pre);
    }

    vector<vi> c(m + 1);
    for (int j = 1, pre; j <= m; j++) {
        pre = 0;
        for (int i = 1; i <= n; i++) {
            if (f[i][j] == 1) pre = pre * 10 + 1;
            else c[j].push_back(pre), c[j].push_back(f[i][j]), pre = 0;
        }
        c[j].push_back(pre);
    }

    vi cntR(n + 1);
    for (int i = 1; i <= n; i++)
        for (auto j: r[i])
            if (j == 11) cntR[i]++;

    vi cntC(m + 1);
    for (int j = 1; j <= m; j++)
        for (auto i: c[j])
            if (i == 11) cntC[j]++;

    vi op(T), res;
    int ans = INF;
    auto calc = [&](const vi &v) -> int {
        vi x;
        for (int i: v) {
            if (i > 0) x.push_back(i);
            else if (op[-i] == 0) x.push_back(-1);
        }
        vi y;
        for (int i = 0; i < x.size(); i++) {
            if (x[i] > 0) y.push_back(x[i]);
            else i++, y.back() *= x[i];
        }
        return accumulate(y.begin(), y.end(), 0ll);
    };

    auto dfs = [&](auto dfs, int i) -> void {
        if (i == T) {
            int sum = 0;
            for (int i = 1; i <= n; i++)
                sum += calc(r[i]);
            for (int j = 1; j <= m; j++)
                sum += calc(c[j]);
            if (sum < ans) ans = sum, res = op;
            return;
        }
        auto [ix, iy] = opt[i];
        if (cntR[ix] >= 2 or cntC[iy] >= 2) {
            op[i] = 1;
            dfs(dfs, i + 1);
        }
        op[i] = 0;
        dfs(dfs, i + 1);
    };

    dfs(dfs, 0);

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (f[i][j] == 1) cout << 1;
            else cout << "*+"[res[-f[i][j]]];
        }
        cout << "\n";
    }
    return 0;
}

C. Seats

这到题目,把\(i\)\(a_i\)连一条有向边,图就是内向基环树森林,其中有两种,一种是链,且最后一个点一定是空节点,此时链上所有点均可满足。另一种是内向基环树,此时只有环上的点可以满足。

所以可以用拓扑序求出链的长度,并且拓扑序没有入队的点就是环上的点。

#include <bits/stdc++.h>

using namespace std;

using i32 = int;
using i64 = long long;

using vi = vector<int>;
using pii = pair<int, int>;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    int N = n * 2;
    vector<vi> e(N + 1);
    vi vis(N + 1), dis(N + 1), deg(N + 1);

    for (int i = 1, x; i <= n; i++)
        cin >> x, e[i].push_back(x), deg[x]++;

    queue<int> q;
    for (int i = 1; i <= N; i++)
        if (deg[i] == 0) q.push(i);

    while (not q.empty()) {
        int x = q.front();
        vis[x] = 1;
        q.pop();
        for (auto y: e[x]) {
            dis[y] = max(dis[x], dis[x] + 1);
            if (--deg[y] == 0) q.push(y);
        }
    }
    int res = 0;
    for (int i = 1; i <= N; i++) {
        if (vis[i] == 0) res++;
        if (e[i].empty()) res += dis[i];
    }
    cout << res;

    return 0;
}

E. Trade Road

可以参考官方题解的证明思路,得到以下引理

不存在市场两两不相同的\(A,B,C,D\),使得商路\(\vec{AB},\vec{CD}\)同时存在

因此,最终的结果一定是有两种

  1. 所有商路要么从\(x\)出发,要么到达\(x\)
  2. \(x\)为顶角的等腰三角形。

因此找到从每一个点出发的最远点,然后枚举\(x\)就好了,我这里找最远点采用了二分,而不是官解的双指针。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using ldb = long double;

#define int i64

using vi = vector<int>;

int K;

int dis(int x, int y) {
    return min(((x - y) % K + K) % K, ((y - x) % K + K) % K);
}

int norm(int x) {
    return (x % K + K) % K;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> K >> n;

    vi a(n + 1);
    map<int, int> idx;
    set<int> vis;
    for (int i = 1; i <= n; i++) {
        cin >> a[i], a[i]--, idx[a[i]] = i;
        vis.insert(a[i] - K - K);
        vis.insert(a[i] - K);
        vis.insert(a[i]);
        vis.insert(a[i] + K);
        vis.insert(a[i] + K + K);
    }
    vector<vi> e(n + 1);
    vector<map<int, int>> road(n + 1);
    vi cnt(n + 1);
    for (int i = 1; i <= n; i++) {
        set<int> b;
        int x = a[i] + K / 2;
        b.insert(norm(*vis.lower_bound(x)));
        b.insert(norm(*prev(vis.upper_bound(x))));
        x++;
        b.insert(norm(*vis.lower_bound(x)));
        b.insert(norm(*prev(vis.upper_bound(x))));

        int maxD = 0;
        for (auto j: b)
            maxD = max(maxD, dis(a[i], j));

        for (auto y: b)
            if (dis(a[i], y) == maxD) {
                int j = idx[y];
                e[i].push_back(j);
                cnt[j]++;
                road[i][j] = 1;
            }
    }

    int res = 0;
    for (int x = 1, ans; x <= n; x++) {
        ans = cnt[x];
        for (auto y: e[x])
            if (road[y][x] == 0) ans++;
        res = max(res, ans);

        if (e[x].size() == 2) {
            int y = e[x][0], z = e[x][1];
            if (road[y][z])
                res = max(res, 3ll);
        }
    }
    cout << res;
    return 0;
}

F. Try a try, AC is OK

与只会变小,所以序列最大值就是答案

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;
using vi = vector<int>;


void solve() {
    int n;
    cin >> n;
    vi a(n);
    for (auto &i: a) cin >> i;
    cout << ranges::max(a) << "\n";
}

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();

    return 0;
}

G. Disappearing Number

一种类似数位 dp 的方法,枚举每一位的方案即可

#include <bits/stdc++.h>

using namespace std;

using i32 = int;
using i64 = long long;

#define int i64

using vi = vector<int>;

void solve() {
    int n, x;
    cin >> n >> x;
    int pre = 1, sum = 1;
    while (n) {
        int y = n % 10;
        if (y > x) y--;
        sum += y * pre;
        pre = pre * 9, n /= 10;

    }
    cout << sum << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();

    return 0;
}

L. Chess

考虑到在任何进制下,个位数一定是成立的,且满足条件的\(y\)个位一定是非\(0\)的。

类似 bash 博弈,如果\(x\)的个位为 0 则一定无法一步拿完,同时如果个位不为 0 ,则一定可以通过一次操作使得个位为 0。因此如果\(x\)的个位为0,则先手一定必败。所以枚举进制,找到最小\(k\)进制使得\(x\)各个位不为 0 即可。

#include <bits/stdc++.h>

using namespace std;

using i32 = int;
using i64 = long long;

using vi = vector<int>;
using pii = pair<int, int>;

void solve() {
    i64 n;
    cin >> n;
    for (int i = 2;; i++)
        if (n % i != 0) {
            cout << i << "\n";
            return;
        }
    return;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();


    return 0;
}

M. Window Decoration

一个正方形的面积为 \(2\),每一个相邻的正方形会公用\(0.5\)的面积,不会出现同一块被三个及以上的正方形覆盖的情况,因此简单容斥就好了

#include <bits/stdc++.h>

using namespace std;

using i32 = int;
using i64 = long long;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;

    vector<pii> a(n);
    for (auto &[x, y]: a)
        cin >> x >> y;

    sort(a.begin(), a.end());
    a.resize(unique(a.begin(), a.end()) - a.begin());

    double res = a.size() * 2;
    for (int i = 1; i < a.size(); i++) {
        auto [x, y] = a[i];
        for (int j = 0; j < i; j++) {
            auto [p, q] = a[j];
            if (abs(x - p) + abs(y - q) == 1) res -= 0.5;
        }
    }
    cout << fixed << setprecision(10) << res;

    return 0;
}
posted @ 2024-08-14 14:26  PHarr  阅读(76)  评论(0编辑  收藏  举报