Loading

[COCI2021-2022#4] Šarenlist

算法

首先, 合法路径上至少有两条边颜色不同, 我们考虑正难则反, 统计不合法的数量, 那么合法数量就等于总可能数 \(-\) 不可能数

中间转化的部分在这里写没有什么意义, 跳转至 Luogu 题解区

那么我们的问题转化成, 如何计算多条路径中, 某些路径不合法的情况数

首先我们需要考虑, 对于每条路径, 我们考虑将其缩成一个点, 方便计算, 因为只要选择了这一个点的颜色, 其他的都确定了 (这里是不合法情况)

这个我们用并查集处理, 在找每条路径时, 记录一下即可

在枚举第 \(i\) 条路径颜色相同并累加上对应情况数时,我们使其它边颜色任选,就会出现其它指定的路径颜色也相同的情况,也就是说,在计算第 \(i\) 种情况时,也会算上一部分 \(j\) 的情况( \(W_i\)\(W_j\) 有交集),或者说,\(W_i\) 实际上是 至少\(i\) 条路径颜色相同的情况

关于如何处理路径不合法的方案数重复计算这一问题, 我们考虑使用容斥原理

使用状压的 \(\rm{trick}\)

代码

#include <bits/stdc++.h>

#define int long long
const int MOD = 1e9 + 7;
const int MAXN = 65;

int n, m, k;

int kpow[MAXN];
int Ans = 0;

std::map<std::pair<int, int>, int> mp;
std::vector<int> e[MAXN], Past[MAXN];

struct DSU_struct
{
    int fa[MAXN];
    int getfa(int x) { return fa[x] == x ? x : fa[x] = getfa(fa[x]); }
    void fa_init() { for (int i = 1; i <= n; i++) fa[i] = i; }
} DSU;

bool dfs(int Now, int to, int fa, int num)
{
    if (Now == to)
        return true;

    for (auto i : e[Now])
    {
        if (i == fa)
            continue;
        if (dfs(i, to, Now, num))
        {
            Past[num].push_back(mp[{Now, i}]);
            return true;
        }
    }
    return false;
}

int calc(int x)
{
    DSU.fa_init();

    int cnt = 0;
    for (int i = 1; i <= m; i++)
        if (x & (1 << (i - 1)))
            for (auto j : Past[i])
                DSU.fa[DSU.getfa(j)] = DSU.getfa(Past[i][0]);
    for (int i = 1; i <= n - 1; i++)
        cnt += (DSU.fa[i] == i);
    return cnt;
}

void init() {
    kpow[0] = 1;
    for (int i = 1; i <= n; i++)
        kpow[i] = kpow[i - 1] * k % MOD;
}

signed main()
{
    scanf("%lld %lld %lld", &n, &m, &k);

    init();

    for (int i = 1; i <= n - 1; i++) {
        int u, v;
        scanf("%lld %lld", &u, &v);
        mp[{u, v}] = mp[{v, u}] = i;
        e[u].push_back(v);
        e[v].push_back(u);
    }

    /*计算简单路径长度*/
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        dfs(u, v, u, i);
    }


    for (int i = 0; i < (1 << m); i++) {
        Ans += ((__builtin_popcount(i) & 1) ? -1 : 1) * kpow[calc(i)] + MOD;
        Ans %= MOD;
    }

    printf("%lld", Ans);
}

总结

正难则反

善于转化问题, 善于利用数据结构

posted @ 2024-11-28 07:38  Yorg  阅读(24)  评论(0)    收藏  举报