[组合] UOJ#308. 【UNR #2】UOJ拯救计划

来源:互联网 发布:iphone手机读书软件 编辑:程序博客网 时间:2024/06/16 03:29

题意

给定一张n个点m条边的无向图,给图的所有顶点染色,使得对于任意一条边,两端的颜色不同。有K种颜色可用。
求染色的方案数模6的值。

题解

挺简单的题目,但是做的时候就没想到……我好菜啊……
就是一个经典的貌似是NPC的问题,然后只需要输出答案%6,使问题特殊化。
哪里特殊了呢?
我们这样考虑答案:

ans=i=1KA(K,i)[i]

我们知道排列数A(K,i)=(Ki+1)(Ki+2)...K
相信大家小学的时候都学过:任意两个连续整数乘积一定有2这个约数,任意三个连续整数乘积一定有3这个约数。
这样就简单了,当i3A(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;} 
原创粉丝点击