2022 ICPC Shenyang

2022 ICPC Shenyang

C

区间越大越好、答案区间的左右端点一定可以是已有的数,然后就可以 n 方做了

E

不太懂啊,虽然过了

i64 pw2[N * N + 5];

std::vector<int> adj[N + 5];
int d[N + 5], back[N + 5]; // back 表示 i 的子树下有多少点与 i 的祖先有边

bool vis[N + 5];
int sz[N + 5];
i64 f[N + 5][N + 5], g[N + 5];

void add(i64 &a, i64 b) {
    a += b;
    if (a >= Mod) {
        a -= Mod;
    }
}

void pre(int cur, int lst) {
    d[cur] = d[lst] + 1;

    for (int &to : adj[cur]) {
        if (to == lst) {
            continue;
        }

        if (d[to] == 0) {
            pre(to, cur);
            back[cur] += back[to];
        }
        else if (d[to] < d[cur]) {
            back[cur] += 1;
            back[to] -= 1;
        }
    }
}

void dfs(int cur, int lst) {
    vis[cur] = true;
    sz[cur] = 1;
    f[cur][1] = 1;
    for (auto &to : adj[cur]) {
        if (to == lst || vis[to]) {
            continue;
        }

        dfs(to, cur);
        for (int i = 1; i <= sz[cur]; ++i) {
            g[i] = f[cur][i];
            f[cur][i] = 0;
        }
        for (int i = 1; i <= sz[cur]; ++i) {
            for (int j = 1; j <= sz[to]; ++j) {
                // i * j - 1 是两个连通块之间可连的边数
                // -1 是因为 cur - to 已经是连起来的了
                add(f[cur][i + j], g[i] * f[to][j] % Mod * pw2[i * j - 1] % Mod);
            }
        }

        // 容斥
        if (back[to] == 0) {
            i64 sum = 0;
            for (int i = 1; i <= sz[to]; ++i) {
                add(sum, f[to][i]);
            }
            sum = Mod - sum;
            for (int i = 1; i <= sz[cur]; ++i) {
                add(f[cur][i], sum * g[i] % Mod);
            }
        }

        sz[cur] += sz[to];
    }
}

void solve() {
    int n = 0, m = 0;
    std::cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int u = 0, v = 0;
        std::cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    pre(1, 0);
    dfs(1, 0);

    i64 ans = 0;
    for (int i = 1; i <= n; ++i) {
        add(ans, f[1][i]);
        // debug("%lld ", f[1][i]);
    }
    i64 inv2 = (Mod + 1) / 2;
    // 消除掉非树边的影响
    for (int i = 0; i < m - (n - 1); ++i) {
        (ans *= inv2) %= Mod;
    }
    std::cout << ans << '\n';
}

L

直接爆搜即可:

std::array<int, 2> n;
// s[0/1][i] 第 0/1 个阵营里面第 i 个人的状态,0 代表血量,1 代表攻击次数
std::array<int, 2> s[2][N];
int atk[2][N];

// u 代表当前攻击的人
std::array<long double, 3> dfs(int u) {
    std::array<long double, 3> res = { 0.0, 0.0, 0.0 };
    int v = u ^ 1; // 被攻击的人
    
    std::array<int, 2> cnt = { 0, 0 };
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < n[i]; ++j) {
            if (s[i][j][0] > 0) {
                cnt[i] += 1;
            }
        }
    }
    // 平局
    if (!cnt[0] && !cnt[1]) {
        res[2] = 1.0;
        return res;
    }
    // 1 胜利
    if (!cnt[0]) {
        res[1] = 1;
        return res;
    }
    // 0 胜利
    if (!cnt[1]) {
        res[0] = 1;
        return res;
    }

    // 找 u 中攻击次数的最小值
    int mn = Inf;
    for (int i = 0; i < n[u]; ++i) {
        if (s[u][i][0] > 0) {
            mn = std::min(mn, s[u][i][1]);
        }
    } 

    for (int i = 0; i < n[u]; ++i) {
        if (s[u][i][0] <= 0 || s[u][i][1] > mn) {
            continue;
        }
        
        // 找第一个活着攻击次数最小的
        int hpu = s[u][i][0];
        int atku = atk[u][i];
        
        for (int j = 0; j < n[v]; ++j) {
            if (s[v][j][0] <= 0) {
                continue;
            }

            int hpv = s[v][j][0];
            int atkv = atk[v][j];

            s[u][i][0] -= atkv;
            s[u][i][1] += 1;
            s[v][j][0] -= atku;

            auto ret = dfs(v);
            for (int k = 0; k < 3; ++k) {
                res[k] += ret[k] / cnt[v];
            }

            s[u][i][0] += atkv;
            s[u][i][1] -= 1;
            s[v][j][0] += atku;
        }

        break;
    }

    return res;
}

std::array<long double, 3> dfs() {
    std::array<long double, 3> res = { 0.0, 0.0, 0.0 };
    auto ret0 = dfs(0);
    auto ret1 = dfs(1);
    for (int i = 0; i < 3; ++i) {
        res[i] = (ret0[i] + ret1[i]) / 2.0;
    }
    return res;
}

void solve() {
    std::cin >> n[0] >> n[1];
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < n[i]; ++j) {
            std::cin >> atk[i][j];
            s[i][j] = { atk[i][j], 0 };
        }
    }

    std::cout << std::fixed << std::setprecision(10);
    if (n[0] > n[1]) {
        for (auto &i : dfs(0)) {
            std::cout << i << '\n';
        }
    }
    else if (n[1] > n[0]) {
        for (auto &i : dfs(1)) {
            std::cout << i << '\n';
        }
    }
    else {
        for (auto &i : dfs()) {
            std::cout << i << '\n';
        }
    }
}
posted @ 2025-10-19 01:34  Young_Cloud  阅读(10)  评论(0)    收藏  举报