「考试总结」2023-02-13 联合省选模拟赛 – Day1
\texttt {爆搜(dfs)}
\texttt {Statement}
给定一个
一个匹配就是一个可以为空的边集,使得每个点至多是其中一条边的端点。
\texttt {Solution}
新增一个零度的点是不会对答案造成影响的,因此可以加点使
记匹配中的边为实边,在 点
因为 每个点至多是其中一条边的端点,所以连上虚边之后每个点的度数不超过
因此对于一个连通块,它只可能是一条链或者一个简单环。
那么对于新匹配,就是若干条链和若干个环组成的简单图。
显然新匹配和原匹配是一一对应的。
考虑在新匹配拆成若干个连通块
一个连通块再分成链和环两种情况计算。
巧妙之处在于通过连虚边的方式转换问题,而新问题中
计算环的方法与
这里只讲计算链的方法。
定义
枚举不在集合
\texttt {Code}
#include <cstdio>
#define LL long long
#define uLL unsigned LL
namespace Read {
static const int buf_size = 1 << 12;
static const bool use_fread = true;
static unsigned char buf[buf_size];
static int buf_len, buf_pos;
bool isEOF() {
if (buf_pos == buf_len) {
buf_pos = 0; buf_len = fread(buf, 1, buf_size, stdin);
if (buf_pos == buf_len) return true;
}
return false;
}
char readChar() {
if (!use_fread) return getchar();
return isEOF() ? EOF : buf[buf_pos++];
}
LL rint() {
LL x = 0, Fx = 1; char c = readChar();
while (c < '0' || c > '9') { Fx ^= (c == '-'); c = readChar(); }
while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = readChar(); }
return Fx ? x : -x;
}
template <typename T>
void read(T &x) {
x = rint();
}
template <typename T, typename... Ts>
void read(T &x, Ts &...rest) {
read(x);
read(rest...);
}
} using namespace Read;
namespace Math {
LL Max(LL u, LL v) { return (u > v) ? u : v; }
LL Min(LL u, LL v) { return (u < v) ? u : v; }
} using namespace Math;
const int mod = 1e9 + 7;
const int inv = (mod + 1) >> 1;
const int MAX_n = 36;
const int MAX_m = 630;
const int mx = 1 << 18;
int n, m, c, up;
int qpow[MAX_n + 5];
int dp[mx + 5][MAX_n + 5];
int dpp[mx + 5][MAX_n + 5];
int dp1[mx + 5], dp2[mx + 5];
bool vis[MAX_n + 5][MAX_n + 5];
int main() {
freopen("dfs.in", "r", stdin);
freopen("dfs.out", "w", stdout);
read(n, m, c); n += (n & 1); up = 1 << (n >>= 1);
for (int i = 1, u, v; i <= m; i++)
read(u, v), --u, --v, vis[u][v] = vis[v][u] = true;
qpow[0] = 1 % mod;
for (int i = 1; i <= n; i++)
qpow[i] = (LL)qpow[i - 1] * c % mod;
for (int i = 0; i < n; i++) {
dp[1 << i][i << 1] = dp[1 << i][i << 1 | 1] = 1;
dpp[1 << i][i << 1 | 1] = 1;
}
for (int S = 1; S < up; S++) {
int siz = 0, id = 0;
for (int i = 0; i < n; i++)
if (S & (1 << i)) ++siz;
for (int i = 0; i < n; i++) {
if (S & (1 << i)) {
int a = i << 1, b = i << 1 | 1;
for (int j = 0; j < n; j++) {
if (S & (1 << j)) continue;
int x = j << 1, y = j << 1 | 1, T = S | (1 << j);
if (vis[a][x]) dp[T][y] = (dp[T][y] + dp[S][a]) % mod;
if (vis[a][y]) dp[T][x] = (dp[T][x] + dp[S][a]) % mod;
if (vis[b][x]) dp[T][y] = (dp[T][y] + dp[S][b]) % mod;
if (vis[b][y]) dp[T][x] = (dp[T][x] + dp[S][b]) % mod;
}
}
}
for (int i = 0; i < n; i++)
if (S & (1 << i)) { id = i; break; }
for (int i = id; i < n; i++) {
if (S & (1 << i)) {
int a = i << 1, b = i << 1 | 1;
for (int j = id + 1; j < n; j++) {
if (S & (1 << j)) continue;
int x = j << 1, y = j << 1 | 1, T = S | (1 << j);
if (vis[a][x]) dpp[T][y] = (dpp[T][y] + dpp[S][a]) % mod;
if (vis[a][y]) dpp[T][x] = (dpp[T][x] + dpp[S][a]) % mod;
if (vis[b][x]) dpp[T][y] = (dpp[T][y] + dpp[S][b]) % mod;
if (vis[b][y]) dpp[T][x] = (dpp[T][x] + dpp[S][b]) % mod;
}
}
}
for (int j = 0; j < n; j++) {
if (S & (1 << j)) {
int x = j << 1, y = j << 1 | 1;
dp1[S] = (dp1[S] + (LL)dp[S][x] * qpow[siz - 1] % mod * inv) % mod;
dp1[S] = (dp1[S] + (LL)dp[S][y] * qpow[siz - 1] % mod * inv) % mod;
}
}
for (int j = id; j < n; j++) {
if (S & (1 << j)) {
int x = j << 1, y = j << 1 | 1;
if (vis[x][id << 1]) dp1[S] = (dp1[S] + (LL)dpp[S][x] * qpow[siz]) % mod;
if (vis[y][id << 1]) dp1[S] = (dp1[S] + (LL)dpp[S][y] * qpow[siz]) % mod;
}
}
}
dp2[0] = 1 % mod;
for (int S = 1; S < up; S++) {
int siz = 0, id = 0;
for (int i = 0; i < n; i++)
if (S & (1 << i)) ++siz, id = i;
for (int T = S; T & (1 << id); T = (S & (T - 1)))
dp2[S] = (dp2[S] + (LL)dp2[S ^ T] * dp1[T]) % mod;
}
printf("%d\n", dp2[up - 1]);
return 0;
}