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

  • 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 ,如果 m0 ans=0 ,否则 ans=1
对于 i=2 ,找出图中联通块的个数 n ,对于固定的两种颜色,共2n中方式,那么 ans=C(k,2)2n
- 注:若 m=0 C(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;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值