题意
给定一张n个点m条边的无向图,给图的所有顶点染色,使得对于任意一条边,两端的颜色不同。有K种颜色可用。
求染色的方案数模6的值。
题解
挺简单的题目,但是做的时候就没想到……我好菜啊……
就是一个经典的貌似是NPC的问题,然后只需要输出答案%6,使问题特殊化。
哪里特殊了呢?
我们这样考虑答案:
ans=∑i=1KA(K,i)∗[刚好用i了种颜色的方案数]
我们知道排列数 A(K,i)=(K−i+1)∗(K−i+2)∗...∗K
相信大家小学的时候都学过:任意两个连续整数乘积一定有2这个约数,任意三个连续整数乘积一定有3这个约数。
这样就简单了,当 i≥3 时 ,A(K,i)%6=0 ,都不用管了。
所以我们只需要考虑用1种颜色和2种颜色即可。
然后随便乱搞一下就行了。
要注意 m=0 的特殊情况。
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
const int maxn=1000005,maxe=4000005;
int Q,n,m,K,c[maxn],fa[maxn],res,cnt;
bool vis[maxn];
int fir[maxn],nxt[maxe],son[maxe],tot;
void add(int x,int y){
son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot;
}
int getfa(int x){ return fa[x]==x?x:fa[x]=getfa(fa[x]); }
void merge(int x,int y){
x=getfa(x); y=getfa(y);
if(x!=y) fa[x]=y;
}
void dfs(int x,int k){
vis[x]=true; c[x]=k;
for(int j=fir[x];j;j=nxt[j]){
if(!vis[son[j]]) dfs(son[j],k^1);
else if(c[son[j]]!=(k^1)) res=0;
}
}
int main(){
freopen("uoj308.in","r",stdin);
freopen("uoj308.out","w",stdout);
scanf("%d",&Q);
while(Q--){
scanf("%d%d%d",&n,&m,&K);
for(int i=1;i<=n;i++) fa[i]=i;
memset(fir,0,sizeof(fir)); tot=0;
for(int i=1;i<=m;i++){
int x,y; scanf("%d%d",&x,&y);
add(x,y); add(y,x); merge(x,y);
}
if(m==0){
res=1; for(int i=1;i<=n-1;i++) res=(res*2)%6; res=(res-1+6)%6;
printf("%d\n",(K%6+(K%6)*(K-1)%6*res%6)%6);
} else{
for(int i=1;i<=n;i++) c[i]=-1;
memset(vis,0,sizeof(vis));
res=1; cnt=0;
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0), cnt+=1;
for(int i=1;i<=cnt-1;i++) res=(res*2)%6;
printf("%d\n",res*(K%6)*(K-1)%6);
}
}
return 0;
}