1. 程式人生 > >[組合] UOJ#308. 【UNR #2】UOJ拯救計劃

[組合] 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這個約數。
這樣就簡單了,當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; }