UOJ 2017NOI Round #2 T1:UOJ拯救计划(排列组合)

来源:互联网 发布:java语言和c语言 编辑:程序博客网 时间:2024/06/16 16:23
  • Description
    小O和小I一直喜欢打 UOJ 的比赛,然而等了半个丁酉年却也没能等到下一次比赛。眼看着 NOI 即将到来,他们决定一探究竟,找出 UOJ 沉寂的真正原因!
    终于有一天,他们得知 UOJ 的管理层全都被两个一心想摧毁 OI 界的大魔王——滴滴诶柳和不响公座给封印起来。
    这两个大魔王向来战略上联手对敌,战术上分工合作。每次滴滴诶柳首先给 oier 带来一堆麻烦;接着不响公座用超声波对 oier 进行催眠,降低 oier 们的反抗效率;关键时候滴滴诶柳又进行反向催眠,让 oier 拼命反击筋疲力尽。两个魔王轮流值班,有着充足的休息时间,而他们的对手却受到无间断攻击。最后随着时间的推移,oier 们的体力到了最低点时,不响公座放出大招,将 oier 封印起来。
    要想拯救 UOJ,必须打败这两个魔王。小O和小I查阅资料,终于找到了获胜的方法——OI 阵。
    首先,他们需要召集 nn 名 oier 布阵,联手对敌。为了高效地反击滴滴诶柳,他们决定让 nn 名 oier 站成一张图的样子,每个 oier 负责应对自己和相邻 oier 所受到的攻击。当一个 oier 受到攻击时,图中相邻的 oier 及时支援。
    同时他们意识到,当一个 oier 身边有同校的 oier 时,不响公座攻击的时候他们会聊起天来从而阵法被破;而反之,如果身边的人都不熟悉,则会产生表现欲,有效抗住不响公座的超声波攻击。因此他们要求,图中任意两个相邻的 oier 来自不同的学校。
    现在已知这张图的构成。该图具有 nn 个点 mm 条边,节点编号依次为 1,…,n1,…,n。同时共有 kk 个学校,由于拯救 UOJ 人人有责,故每个学校都有无数的 oier 愿意出力。
    小O想要知道有多少种布阵方式,但是鉴于小I最多只能数到 55(他学会的最大的数字来自于“一二三四五上山打老虎”),因此小O决定输出方案数模 6。
    两个布阵方式被认为是不同的当且仅当存在一个节点 ii 使得这两种布阵方式中守卫该节点的 oier 来自不同的学校。

  • Input
    第一行一个正整数 TT,表示数据组数。
    对于每组数据,第一行三个整数 n,m,kn,m,k,分别表示图的点数、边数、总共的学校数。
    接下来 mm行,每行两个整数 a,ba,b,表示 a,ba,b 间有一条边。保证 1≤a,b≤n1≤a,b≤n 且 a≠ba≠b。

  • Output
    对于每组数据,输出一个数表示该组数据的答案。

  • Sample Input
    2
    5 4 5
    1 2
    1 3
    1 4
    1 5
    8 7 2
    1 2
    2 3
    3 4
    4 5
    5 6
    6 7
    7 8

  • Sample Output
    2
    2

  • 题解:

其实题意翻译过来就是给你n个点,m条边(可能不连通),k种颜色,求使相邻两点不同颜色的染色方案数(mod6)。

因为有mod6,所以这道题变得简单了起来。

由题意可得ans=A(k,i)i,所谓本质不同即使指将图中所有同种颜色的点替换为其他颜色后,两种染色方式不可能相同。

ans=A(k,i)

对于i3,6|A(k,i),所以不予考虑。
对于i=1,如果m0ans=0,否则ans=1
对于i=2,找出图中联通块的个数n,对于固定的两种颜色,共2n中方式,那么ans=C(k,2)2n
- 注:若m=0C(k,2)(2n2)。(排除全为一种颜色)。其实可以先处理m等于1的情况,即所有点都独立,此时ans=kn

  • Code:
#include<bits/stdc++.h>using namespace std;const int Maxn=4e5+60;inline int read(){    char ch=getchar();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}    return i*f;}int T,n,m,k;int last[Maxn],before[Maxn],to[Maxn],ecnt,vis[Maxn];inline void add(int x,int y){    ecnt++;    before[ecnt]=last[x];    last[x]=ecnt;    to[ecnt]=y;}inline int power(int x,int y){    int res=1;    while(y)    {        if(y&1)res=((res*x)%6);        x=(x*x)%6;        y>>=1;    }    return res;}inline bool dfs(int i,int col){    vis[i]=col;    for(int e=last[i];e;e=before[e])    {        if(!vis[to[e]]){if(!dfs(to[e],-col))return false;}        else if(vis[to[e]]==vis[i])return false;    }        return true;}int main(){    T=read();    while(T--)    {        n=read(),m=read(),k=read();        int ans=0;        if(m==0)        {            cout<<power(k,n)<<endl;        }        else        {            if(k==1)            {                for(int i=1;i<=m;i++)read(),read();                ans=0;            }            else            {                memset(vis,0,sizeof(vis));                memset(last,0,sizeof(last));                ecnt=0,ans=1;                int cnt=0;                int res1=(k*(k-1)/2)%6;                for(int i=1;i<=m;i++)                {                    int x=read(),y=read();                    add(x,y),add(y,x);                }                for(int i=1;i<=n;i++)                {                    if(!vis[i])                    {                        if(dfs(i,1))++cnt;                        else                        {                            ans=0;break;                        }                    }                }                ans=(res1*power(2,cnt)*ans%6);            }            cout<<ans<<endl;        }    }}