主要思路
看到这个很小的模数就知道肯定是个 Trick 题,一定不会让你去跑一般图的(估计是 NPC)。
首先,我们可以设颜色未标号的、用了 \(i\) 种颜色的方案数为 \(f_i\),那么答案就是:
\[ ans = \sum_{i = 1}^k f_i k^{\underline{i}} \]
发现这个东西在 \(i > 2\) 时就直接被模成了 \(0\)。我们只需要算 \(f_1, f_2\) 即可。其中 \(f_1 = [m = 0]\)。\(f_2\) 的方案数等价于二分图的双部染色的方案数。所以,我们跑一边二分图然后算 \(2^b\) 即可,\(b\) 是连通块次数。
代码
// UOJ308.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e5 + 200; int T, n, m, k, head[MAX_N], current, col[MAX_N]; struct edge { int to, nxt; } edges[MAX_N << 2]; void addpath(int src, int dst) { edges[current].to = dst, edges[current].nxt = head[src]; head[src] = current++; } int fpow(int bas, int tim) { int ret = 1; while (tim) { if (tim & 1) ret = 1LL * ret * bas % 6; bas = 1LL * bas * bas % 6; tim >>= 1; } return ret; } bool dfs(int u, int c) { col[u] = c; for (int i = head[u]; i != -1; i = edges[i].nxt) if (col[edges[i].to] == -1 && !dfs(edges[i].to, c ^ 1)) return false; else if (col[edges[i].to] != -1 && col[edges[i].to] == c) return false; return true; } int main() { scanf("%d", &T); while (T--) { scanf("%d%d%d", &n, &m, &k); memset(head, -1, sizeof(int) * (n + 10)); memset(col, -1, sizeof(int) * (n + 10)); current = 0; if (m == 0) { printf("%d\n", fpow(k, n)); continue; } for (int i = 1, u, v; i <= m; i++) scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u); int acc = 2; bool flag = true; for (int i = 1; i <= n; i++) if (col[i] == -1) flag &= dfs(i, 0), acc = 2LL * acc % 6; if (!flag) { puts("0"); continue; } printf("%lld\n", 1LL * k * acc % 6 * (k - 1) % 6); } return 0; }