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

题意

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

题解

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

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

我们知道排列数 A(K,i)=(Ki+1)(Ki+2)...K
相信大家小学的时候都学过:任意两个连续整数乘积一定有2这个约数,任意三个连续整数乘积一定有3这个约数。
这样就简单了,当 i3 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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值