2022 年牛客多校第十场签到题题解

Shannon Switching Game?

https://ac.nowcoder.com/acm/contest/33195/F

F Shannon Switching Game?

题意:给定一个多重无向图 G(V,E)G(V,E) 和一对点 s,ts,t,起始时 ss 处有一个令牌。先手可以选择删除图上的一条边,后手可以选择删除令牌所在点 uu 邻接的一条边 (u,v)(u,v),然后将令牌移动到 vv。当令牌移动到 tt 时后手获胜,否则先手胜。问最终结局。V100,E1×103|V| \leq 100,|E| \leq 1\times 10^3

解法:若令牌所处的位置确定,则胜负已分。显然若令牌在 tt 处为一个必胜态,若某个节点能到达两个及以上的必胜态,则该节点也为必胜态——因为先手没办法删完该点通向必胜态的边。所以倒过来从 tt 开始 bfs 找必胜态节点即可。总时间复杂度 O(V+E)\mathcal O(|V|+|E|)

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int caset, n, m, s, t;
    scanf("%d", &caset);
    while (caset--)
    {
        scanf("%d%d%d%d", &n, &m, &s, &t);
        vector<vector<int>> deg(n + 1, vector<int>(n + 1, 0));
        vector<vector<int>> g(n + 1);
        for (int i = 1, u, v; i <= m; i++)
        {
            scanf("%d%d", &u, &v);
            deg[u][v]++;
            deg[v][u]++;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        vector<int> vis(n + 1), in(n + 1, 0);
        queue<int> q;
        vis[t] = 1;
        q.push(t);
        while (!q.empty())
        {
            int tp = q.front();
            q.pop();
            for (auto i : g[tp])
            {
                if (deg[tp][i] > 1)
                {
                    if (!vis[i] && vis[tp])
                    {
                        vis[i] = 1;
                        q.push(i);
                    }
                }
                else if (deg[tp][i] == 1 && vis[tp])
                    in[i]++;
                if (in[i] >= 2 && !vis[i])
                {
                    vis[i] = 1;
                    q.push(i);
                }
            }
        }
        if (vis[s])
            printf("Join Player\n");
        else
            printf("Cut Player\n");
    }
    return 0;
}

H Wheel of Fortune

题意:给定 1616 个数字 A,B,a1,a2,,a7,b1,b2,,b7A,B,a_1,a_2,\cdots,a_7,b_1,b_2,\cdots,b_7,从中随机选择一个数字 xx 使得 xx10x \leftarrow x-10,直到 A0A \leq 0 或者 B0B \leq 0。问有多大概率 B0B \leq 0 的结束游戏。

解法:显然随机选择数字只和 A,BA,B 有关。记 x=A10x=\left\lceil \dfrac{A}{10} \right \rceily=B10y=\left\lceil \dfrac{B}{10} \right \rceil,则只需要让 BB 被选择 yy 次之前 AA 不被选择到 xx 次即可,且最后一击必须是抽中 BB。答案为 i=0x1(i+y1i)2(i+y)\displaystyle \sum_{i=0}^{x-1} {i+y-1 \choose i}2^{-(i+y)}

I Yet Another FFT Problem?

题意:给定长度分别为 n,mn,m 的序列 {an},{bm}\{a_n\},\{b_m\},问是否存在四个下标 i,j,k,li,j,k,l 满足 1ijn,1klm1 \leq i \leq j \leq n,1 \leq k \leq l \leq maiaj=bkbl|a_i-a_j|=|b_k-b_l|ai,bi1×107a_i,b_i \leq 1\times 10^7n,m1×106n,m \leq 1\times 10^6

解法:不妨减少限制 iji \leq jklk \leq l,则可脱去绝对值有 aiaj=bkbla_i-a_j=b_k-b_l,即 ai+bl=aj+bka_i+b_l=a_j+b_k。那么问题转化为,从 {an},{bm}\{a_n\},\{b_m\} 中分别选一个数使得他们碰撞。由于 ai,bi1×107a_i,b_i \leq 1\times 10^7,则 ai+bj[1,2×107]a_i+b_j \in [1,2\times 10^7],因而用鸽巢原理可得,{b}\{b\} 序列长度不超过 2×107n\left\lceil \dfrac{2\times 10^7}{n}\right \rceil 就必然产生碰撞。所以仅需要保留 bb 序列的前 2×107n\left\lceil \dfrac{2\times 10^7}{n}\right \rceil 项即可。总时间复杂度 O(V)O(V),其中 VV 代表 a,ba,b 的值域。

#include <bits/stdc++.h>
#define fp(i, a, b) for (int i = a, i##_ = (b) + 1; i < i##_; ++i)
#define fd(i, a, b) for (int i = a, i##_ = (b) - 1; i > i##_; --i)

using namespace std;
const int N = 2e7 + 5;
using pii = pair<int, int>;
int n, m;
pii w[N];
unordered_map<int, vector<int>> A, B;
void Solve() {   
    scanf("%d%d", &n, &m);
    for (int x, i = 1; i <= n; ++i) scanf("%d", &x), A[x].push_back(i);
    for (int x, i = 1; i <= m; ++i) scanf("%d", &x), B[x].push_back(i);
    vector<pii> a, b;
    pii ma, mb;
    for (auto &[i, v] : A) {
        a.push_back({i, v[0]});
        if (v.size() > 1) ma = {v[0], v[1]};
    }
    for (auto &[i, v] : B) {
        b.push_back({i, v[0]});
        if (v.size() > 1) mb = {v[0], v[1]};
    }
    if (ma.first && mb.first)
        return printf("%d %d %d %d\n", ma.first, ma.second, mb.first, mb.second), void();
    b.resi***(b.size(), size_t(2e7 / a.size())));
    for (auto &[x, i] : a)
        for (auto &[y, k] : b) {
        auto [j, l] = w[x + y];
        if (j && i != j && k != l) {
            printf("%d %d %d %d\n", i, j, k, l);
            return;
        } else w[x + y] = {i, k};
    }
    puts("-1");
}
int main() {
    int t = 1;
    while (t--) Solve();
    return 0;
}
全部评论

相关推荐

11-09 12:17
清华大学 C++
out11Man:小丑罢了,不用理会
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务