CF11D A Simple Task 题解

一、题目:

洛谷原题

二、思路:

状压DP。

\(f(x,s)\)表示走过点的集合为\(s\),当前在\(x\)这个点的所有的方案数。为了避免重复,我们强制链的起点为链上所有点最小的那一个。

设边\((x,y)\)\(y\)有以下几种情况。

  1. \(y\)小于\(s\)中编号最小的点。那么如果让\(s\)这条链和\(y\)连起来,那么新的链就会和以\(y\)为起点的链重复,不予考虑。
  2. \(y\)\(s\)中编号最小的点。说明找到了环,令\(ans=ans+f(x,s)\)
  3. \(y\)\(s\)中编号最小的点大,但\(y\)不在\(s\)中。则\(f(y,s\cup \{y\})=f(y,s\cup \{y\})+f(x,s)\).
  4. \(y\)\(s\)中编号最小的点大,但\(y\)\(s\)中。虽然得到了一个环,但是这个环之前统计过,不予考虑。

然后我们发现\(ans\)多算了一部分。多算了只有两个点一条边的情况以及每个环多算了两次。

所以最终的答案就是\((ans-m)/2\)

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <bitset>

using namespace std;

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 20;

int n, m, head[maxn], tot;
long long ans;
long long f[maxn][1 << maxn];

struct Edge {
    int y, next;
    Edge() {}
    Edge(int _y, int _next) : y(_y), next(_next) {}
}e[maxn * maxn];

vector<int>num[maxn];

inline void connect(int x, int y) {
    e[++tot] = Edge(y, head[x]);
    head[x] = tot;
}

inline int call(int s, int i) {
    return (s >> i) & 1;
}

inline int count(int s) {
    int res = 0;
    for (int i = 0; i < n; ++i) {
        if (call(s, i)) ++res;
    }
    return res;
}

inline int lowbit(int x) { return x & (-x); }

int main() {
    n = read(); m = read();
    for (int i = 1; i <= m; ++i) {
        int x = read(), y = read();
        --x; --y;
        connect(x, y); connect(y, x);
    }
    for (int s = 0; s < (1 << n); ++s) {
        num[count(s)].push_back(s);
    }
    for (int x = 0; x < n; ++x)
        f[x][1 << x] = 1;
    for (int cnt = 1; cnt <= n; ++cnt) {
        for (int idx = 0; idx < (int)num[cnt].size(); ++idx) {
            int s = num[cnt][idx];
            for (int x = 0; x < n; ++x) {
                if (!call(s, x)) continue;
                for (int i = head[x]; i; i = e[i].next) {
                    int y = e[i].y;
                    if (lowbit(s) > (1 << y)) continue;
                    if (lowbit(s) == (1 << y)) {
                        ans += f[x][s];
                    }
                    if (!call(s, y))
                        f[y][s | (1 << y)] += f[x][s];
                }
            }
        }
    }
    ans -= m;
    printf("%lld\n", ans / 2);
    return 0;
}

posted @ 2021-03-22 21:20  蓝田日暖玉生烟  阅读(50)  评论(0编辑  收藏  举报