2023ICPC网络赛第一场vp + 补题

solved 5 problems

L、D、G:@dbywsc

A、 I:@xmjlove

L. KaChang!

思路

签到题,注意一下 \(k > 2\) 就行。

代码

void solve(void) {
    int n, t; std::cin >> n >> t;
    std::vector<int> a(n);
    for(int i = 0; i < n; i++) std::cin >> a[i];
    int maxn = *std::max_element(a.begin(), a.end());
    std::cout << std::max(2, (int)std::ceil(maxn * 1.0 / t));
}

D.Transitivity

思路

分情况讨论,如果整张图只有一个连通块,那么扩充到完全图即可;如果整张图有多个连通块,且这些连通块内部不传递,那么分别给它们扩充到完全图;如果整张图是多个内部传递的连通块,就选两个最小的连通块连起来,此时需要两两连边。

维护的过程用并查集。

代码

struct DSU {
    std::vector<int> p, siz;
    DSU(int n): p(n + 1), siz(n + 1, 1) {
        std::iota(p.begin(), p.end(), 0);
    }
    int find(int x) {
        if(x == p[x]) return p[x];
        return p[x] = find(p[x]);
    }
    void unite(int a, int b) {
        int pa = find(a), pb = find(b);
        if(pa == pb) return;
        if(siz[pa] < siz[pb]) std::swap(pa, pb);
        p[pb] = pa;
        siz[pa] += siz[pb];
    }
    bool same(int u, int v) {return find(u) == find(v);}
    int size(int u) {return siz[find(u)];}
};
void solve(void) {
    int n, m; std::cin >> n >> m;
    std::vector<PII> a(m);
    DSU dsu(n + 1);
    std::vector<int> ecnt(n + 1);
    for(int i = 0; i < m; i++) std::cin >> a[i].x >> a[i].y;
    for(int i = 0; i < m; i++) dsu.unite(a[i].x, a[i].y);
    for(int i = 0; i < m; i++) {
        ecnt[dsu.find(a[i].x)]++;
    }
    std::vector<PII> comps;
    for(int i = 1; i <= n; i++) {
        if(dsu.find(i) == i) {
            comps.emplace_back(dsu.size(i), ecnt[i]);
        }
    }
    i64 mm = 0;
    for(auto [i, j] : comps) {
        mm += (i * (i - 1) / 2) - j;
    }
    if(comps.size() == 1) {
        std::cout << mm << '\n';
    } else {
        if(mm > 0) std::cout << mm << '\n';
        else {
            std::sort(comps.begin(), comps.end());
            std::cout << comps[0].x * comps[1].x;
        }
    }
}

G.Spanning Tree

思路

分子显然只有一种,所以只需要计算分母有多少种情况就行。

维护两个并查集,第一个并查集模拟加边的过程,加边过程中计算分母。第二个并查集验证给定的生成树能否通过上面的操作生成。如果验证不通过答案就为 \(0\) ,否则输出分数即可。

代码

struct DSU {
    std::vector<int> p, siz;
    DSU(int n): p(n + 1), siz(n + 1, 1) {
        std::iota(p.begin(), p.end(), 0);
    }
    int find(int x) {
        if(x == p[x]) return p[x];
        return p[x] = find(p[x]);
    }
    void unite(int a, int b) {
        int pa = find(a), pb = find(b);
        if(pa == pb) return;
        if(siz[pa] < siz[pb]) std::swap(pa, pb);
        p[pb] = pa;
        siz[pa] += siz[pb];
    }
    bool same(int u, int v) {return find(u) == find(v);}
    int size(int u) {return siz[find(u)];}
};
std::vector<int> G[N];
i64 qpow(i64 a, i64 b = P - 2) {
    i64 res = 1;
    while(b) {
        if(b & 1) res = res * a % P;
        a = a * a % P;
        b >>= 1;
    }
    return res;
}
void solve(void) {
    int n; std::cin >> n;
    if(n == 1) {
        std::cout << "1\n";
        return;
    }
    std::vector<PII> a(n);
    for(int i = 0; i < n - 1; i++) std::cin >> a[i].x >> a[i].y;
    for(int i = 0; i < n - 1; i++) {
        int u, v; std::cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    DSU dsu(n);
    i64 ans = 1;
    for(int i = 0; i < n - 1; i++) {
        int x = a[i].x, y = a[i].y;
        ans = ans * dsu.size(x) % P * dsu.size(y) % P;
        dsu.unite(x, y);
    }
    DSU dsu2(n);
    for(int i = 0; i < n - 1; i++) {
        int x = a[i].x, y = a[i].y;
        if(dsu2.same(x, y)) {
            std::cout << "0\n";
            return;
        }
        int px = dsu2.find(x), py = dsu2.find(y);
        if(G[px].size() < G[py].size()) std::swap(px, py);
        int cnt = 0;
        for(auto v : G[py]) {
            if(dsu2.find(v) == px) {
                cnt++;
                if(cnt > 1) break;
            }
        }
        if(cnt != 1) {
            std::cout << "0\n";
            return;
        }
        //dsu2.unite(px, py);
        dsu2.p[py]   = px;
        dsu2.siz[px] += dsu2.siz[py];
        for(auto v : G[py]) {
            int pv = dsu2.find(v);
            if(pv != px) {
                G[px].push_back(pv);
            }
        }
        std::vector<int>().swap(G[py]);
    }
    std::cout << qpow(ans) << '\n';
}

A.Qualifiers Ranking Rules

思路

签到题,两场读取的时候分别进行排名和去重,最后再合并加去重即可

代码

String[] input = br.readLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
HashMap<Integer, String> dict1 = new HashMap<>();
HashSet<String> set1 = new HashSet<>();
int cnt = 1;
for (int i = 0; i < n; i++) {
    String school = br.readLine();
    if (!set1.contains(school)) {
        dict1.put(cnt++, school);
        set1.add(school);
    }
}
cnt = 1;
HashMap<Integer, String> dict2 = new HashMap<>();
HashSet<String> set2 = new HashSet<>();
for (int i = 0; i < m; i++) {
    String school = br.readLine();
    if (!set2.contains(school)) {
        dict2.put(cnt++, school);
        set2.add(school);
    }
}
HashMap<Integer, String> ans = new HashMap<>();
HashSet<String> set3 = new HashSet<>();
cnt = 1;
for (int i = 1; i <= Math.max(n, m); i++) {
    if (dict1.get(i) != null) {
        if (!set3.contains(dict1.get(i))) {
            ans.put(cnt++, dict1.get(i));
            set3.add(dict1.get(i));
        }
    }
    if (dict2.get(i) != null) {
        if (!set3.contains(dict2.get(i))) {
            ans.put(cnt++, dict2.get(i));
            set3.add(dict2.get(i));
        }
    }
}
for (int i = 1; i <= set3.size(); i++) {
    pw.println(ans.get(i));
}

I. Pa?sWorD

思路

\(dp [i] [j] [k]\) 表示来到密码的第一个位置,当前字符为 \(j\) ,对于三个条件的满足状况为 \(k\) 的情况数

\(dp [i] [j] [k]\) 可以从 \(dp [i - 1] [x] [k]\)\(dp [i - 1] [x] [k ^ (1 << i)]\) 转移过来,\(x\) 表示不为 \(j\) 的所有字符,\(i\) 表示当前的字符可以帮助满足哪个条件

即一个状态可以通过上一个位置 ,字符不等于当前字符,状态等于当前状态,或者等于当前状态取消掉本位置字符的影响后的状态转移过来

时间复杂度为 \(O(n \times 字符数量 ^ 2 \times 状态数)\) ,字符数量为 \(62\) ,状态数为 \(7\) ,所以为 \(O(n \times 3844 \times 7)\)

其中转移的代价为 \(O(62)\) ,我们可以预处理好上一个位置上所有状态的情况数和,这样代价就变成了 \(O(1)\)

代码

int n = Integer.parseInt(br.readLine());
int MOD = 998244353;
char[] s = br.readLine().toCharArray();
HashMap<Character, Integer> dict = new HashMap<>();
for (char i = '0'; i <= '9'; i++) {
    dict.put(i, i - '0');
}
for (char i = 'a'; i <= 'z'; i++) {
    dict.put(i, i - 'a' + 10);
}
for (char i = 'A'; i <= 'Z'; i++) {
    dict.put(i, i - 'A' + 36);
}
int[][] dp = new int[62][1 << 4];
int[] sum2 = new int[1 << 4];
int[][] sum3 = new int[62][1 << 4];
sum2[0] = 1;
for (int i = 0; i < n; i++) {
    int[] nsum2 = new int[1 << 4];
    int[][] nsum3 = new int[62][1 << 3];
    for (int j = 0; j < 62; j++) {
        if (s[i] >= 'A' && s[i] <= 'Z' && j != dict.get(s[i]))
            continue;
        if (s[i] >= '0' && s[i] <= '9' && j != dict.get(s[i]))
            continue;
        if (s[i] >= 'a' && s[i] <= 'z' && j != dict.get(s[i]) && j != dict.get(s[i]) + 26)
            continue;
        for (int k = (1 << 3) - 1; k >= 0; k--) {
            if (j >= 36 && (k & 1) == 0) {
                continue;
            } else if (j >= 10 && j < 36 && (k & (1 << 1)) == 0) {
                continue;
            } else if (j < 10 && (k & (1 << 2)) == 0) {
                continue;
            }
            if (j >= 36) {
                dp[j][k] = (sum2[k] + sum2[k ^ 1]) % MOD;
                dp[j][k] = (dp[j][k] - sum3[j][k] + MOD) % MOD;
                dp[j][k] = (dp[j][k] - sum3[j][k ^ 1] + MOD) % MOD;
            } else if (j >= 10) {
                dp[j][k] = (sum2[k] + sum2[k ^ (1 << 1)]) % MOD;
                dp[j][k] = (dp[j][k] - sum3[j][k] + MOD) % MOD;
                dp[j][k] = (dp[j][k] - sum3[j][k ^ (1 << 1)] + MOD) % MOD;
            } else {
                dp[j][k] = (sum2[k] + sum2[k ^ (1 << 2)]) % MOD;
                dp[j][k] = (dp[j][k] - sum3[j][k] + MOD) % MOD;
                dp[j][k] = (dp[j][k] - sum3[j][k ^ (1 << 2)] + MOD) % MOD;
            }
            nsum2[k] += dp[j][k];
            nsum2[k] %= MOD;
            nsum3[j][k] = dp[j][k];
        }
    }
    sum2 = Arrays.copyOf(nsum2, 1 << 4);
    for (int j = 0; j < 62; j++) {
        sum3[j] = Arrays.copyOf(nsum3[j], 1 << 4);
    }
}
pw.println(sum2[(1 << 3) - 1]);
pw.flush();
posted @ 2025-08-25 18:46  dbywsc  阅读(73)  评论(0)    收藏  举报