AtCoder Beginner Contest 222(补题)

Posted 佐鼬Jun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 222(补题)相关的知识,希望对你有一定的参考价值。

E - Red and Blue Tree

题目链接: link.

题意:

给一颗 n n n个结点, n − 1 n-1 n1条边的树,每条边都有可能是红色或者蓝色,边的总颜色方案为 2 n − 1 2^{n-1} 2n1,现在有一个长度为 m m m的序列, A 1 , . . . . A m A_1,....A_m A1,....Am,现在以 A i A_i Ai为根节点,通过最短路走到 A i + 1 A_{i+1} Ai+1点,最终以每个 A i A_i Ai为根节点跑完路径,算出经过的总红边数和总蓝边数,现在要求总红边数 − - 总蓝边数 = = = K K K,有多少方案满足要求

思路:

把序列 A i A_i Ai跑完,通过 D F S DFS DFS算出每个边都被计算了几遍,然后把所有的经过次数累加,记为 S S S
此时经过的总红边数 + + +总蓝边数= S S S
总红边数 − - 总蓝边数 = = = K K K
此时如果根据两个式子可得, S S S K K K一定同奇偶,如果不是就矛盾,即没有方案,如果 S + K < 0 S+K<0 S+K<0也是矛盾的,也是没有方案。
如果有解,这两个等式,一定能推出, 总 红 边 数 = S + K 2 总红边数= \\frac{S+K}{2} =2S+K,现在问题就转换为,给你每个边被经过的次数,现在让你选出一些数,让它们的总和为总红边数,此时问题就变成了简单的背包模型。跑个 D P DP DP即可

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
const ll mod = 998244353;
typedef pair<int, int> PII;
vector<vector<PII> > e;
vector<int> sum;
ll f[N];
int n, m, k;
bool dfs(int u, int fa, int goal) {
    if (u == goal) return 1;
    for (auto it : e[u]) {
        int v = it.first;
        if (v == fa) continue;
        if (dfs(v, u, goal)) {
            sum[it.second]++;
            return 1;
        }
    }
    return 0;
}
int main() {
    cin >> n >> m >> k;
    vector<int> a;
    a.resize(m);
    for (int i = 0; i < m; i++) {
        cin >> a[i];
        a[i]--;
    }
    e.resize(n);
    for (int i = 0; i < n - 1; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        x--, y--;
        e[x].push_back({y, i});
        e[y].push_back({x, i});
    }
    sum.resize(N - 1);
    for (int i = 0; i < m - 1; i++) {
        dfs(a[i], -1, a[i + 1]);
    }

    int Sum = 0;
    for (int i = 0; i < n - 1; i++) {
        Sum += sum[i];
    }
    if ((Sum + k) % 2 == 1 || Sum < abs(k)) {
        puts("0");
        return 0;
    }
    Sum = abs(Sum + k) / 2;
    f[0] = 1;
    for (int i = 0; i < n - 1; i++) {
        for (int j = Sum; j >= sum[i]; j--) {
            (f[j] += f[j - sum[i]]) %= mod;
        }
    }
    printf("%lld\\n", f[Sum]);
    return 0;
}

F - Expensive Expense

题目链接: link.

题意:

给有 N N N个点的图,有 N − 1 N-1 N1条有权无向边,并且每个点都有自己的点权,现在一个人要旅行,旅行的费用从S点开始,在E点结束,旅行的总分费用就是S点到E点走最短路径的边权总费用加上E点的点权,现在让你输出 N N N行,每行输出从该点出发旅游,走的路径中费用的最大值。

思路:

由于是 N N N个点的图,且有 N − 1 N-1 N1条边的连通图,所以这个无向图也是一颗树。对于一课树来说,是有直径的,也就是树中最长的路径,把图中的点权,也转换为边权,也就是对于点 S S S的点权,就当作 S S S点与 S ′ S' S虚拟点连了一条边,此时求出树的直径,并把树的直径的两个端点求出来,那么此时对于直径端点 u u u v v v来说,最长路径一定就是其算出来的直径。对于不是直径端点的点 t t t,其最长路径一定就是 m a x ( d i s [ u ] [ t ] , d i s [ v ] [ t ] ) max(dis[u][t],dis[v][t]) max(dis[u][t],dis[v][t]),这对于树来说这是一定的。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> PII;
typedef pair<long long, int> PLI;
const int N = 4e5 + 10, M = N * 2;
const ll inf = 0x3f3f3f3f3f3f3f;
int h[M], e[M], ne[M], idx;
ll w[M];
bool vis[M];
ll Dis = -1, cur = -1;
int n;
void clear_graph() {
    memset(h, -1, sizeof(h));
    idx = 0;
}
void add_edge(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int now, int fa, ll d) {
    if (d > Dis) {
        Dis = d, cur = now;
    }
    for (int i = h[now]; ~i; i = ne[i]) {
        int j = e[i];
        ll val = w[i];
        if (j == fa) continue;
        dfs(j, now, d + val);
    }
}

vector<ll> dijkstra(int start) {
    vector<ll> dis(n * 2 + 1, inf);
    memset(vis, 0, sizeof(vis));
    priority_queue<PLI, vector<PLI>, greater<PLI> > q;
    q.push({0, start});
    dis[start] = 0;
    while (q.size()) {
        auto t = q.top();
        q.pop();
        int now = t.second;
        ll distance = t.first;
        if (vis[now]) continue;
        vis[now] = 1;
        for (int i = h[now]; ~i; i = ne[i]) {
            int j = e[i];
            ll val = w[i];
            if (dis[j] > dis[now] + val) {
                dis[j] = dis[now] + val;
                q.push({dis[j], j});
            }
        }
    }
    return dis;
}
int main() {
    clear_graph();
    scanf("%d", &n);
    for (int i = 1, a, b, c; i <= n - 1; i++) {
        scanf("%d%d%d", &a, &b, &c);
        add_edge(a, b, c), add_edge(b, a, c);
    }

    for (int i = 1, d; i <= n; i++) {
        scanf("%d", &d);
        add_edge(i, i + n, d);
        add_edge(i + n, i, d);
    }
    int u, v;
    dfs(1, -1, (ll)0);
    u = cur;
    Dis = -1;
    dfs(cur, -1, (ll)0);
    v = cur;
    auto dis1 = dijkstra(u);
    auto dis2 = dijkstra(v);

    for (int i = 1; i <= n; i++) {
        ll cost = -1;
        if (i == u - n)
            cost = dis2[i];
        else if (i == v - n)
            cost = dis1[i];
        else
            cost = max(dis1[i], dis2[i]);
        printf("%lld\\n", cost);
    }
    return 0;
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

以上是关于AtCoder Beginner Contest 222(补题)的主要内容,如果未能解决你的问题,请参考以下文章

AtCoder Beginner Contest 234

AtCoder Beginner Contest 115 题解

AtCoder Beginner Contest 154 题解

AtCoder Beginner Contest 103

AtCoder Beginner Contest 228

AtCoder Beginner Contest 242