Uoj308【UNR #2】UOJ拯救计划
分析:比较难分析的一道题,先把式子写出来,ans=∑C(k,i)*f(i),f(i)是选i个颜色的方案数.这个模数有点奇怪,比较小而且是合数,说不定就会有某种规律,如果i >= 3,可以发现C(k,i)一定是被6整除的,那么我们只需要考虑i=2和i=1的情况,i=1的情况比较好处理,这种情况下,m只有等于0,答案为k^n,然后可以发现,这不仅仅是对i=1的情况的分析,所以我们要先特判m=0.
那么i=2的情况要怎么处理呢?把每个连通块单独分析,如果一个连通块有一个合法方案,反过来又是一个合法方案,所以一个连通块要么没有贡献,要么就是2,我们只需要把有贡献的连通块的个数cnt求出来,答案就是C(k,2)*2^cnt.一旦有一个连通块没有合法方案,那么答案就直接为0了.
二分图方案数要一个一个连通块考虑,求方案数如果不用dp先写出式子,然后分析.如果模数非常奇怪,找找看有没有什么规律.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std; int T,n,m,k,head[100010],nextt[400010],to[400010],tot = 1; int col[100010],ans; bool flag = false; void add(int x,int y) { to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } int qpow(int a,int b) { int res = 1; while (b) { if (b & 1) res = (res * a) % 6; b >>= 1; a = (a * a) % 6; } return res; } void dfs(int x,int c) { col[x] = c; for (int i = head[x];i;i = nextt[i]) { int v = to[i]; if (col[v]) { if (col[v] == col[x]) { flag = 1; break; } } else dfs(v,3 - c); } } int main() { scanf("%d",&T); while (T--) { memset(head,0,sizeof(head)); memset(col,0,sizeof(col)); ans = 1; tot = 1; flag = 0; scanf("%d%d%d",&n,&m,&k); if (m == 0) printf("%d\n",qpow(k,n)); else { for (int i = 1; i <= m; i++) { int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } for (int i = 1; i <= n; i++) { if (!col[i]) { dfs(i,1); if (flag) { ans = 0; break; } ans *= 2; ans %= 6; } } printf("%d\n",((((k - 1) * k / 2)% 6) * ans) % 6); } } return 0; }