VIJOS1413Valentine’s Present

题目描述

今天是情人节,小杉已经想好了要给喜欢的人送一份特殊的情人节礼物。
礼物是n个颜色各异的箱子,每个箱子里装一个蛋糕,蛋糕是可以上色的。
一个可爱的上色方案应该满足如下条件:
1. 任意一个蛋糕上的颜色应与一个箱子相同(可以是装它的那个箱子的颜色)。
2. 任意开启一个箱子,按里面蛋糕的颜色打开对应的箱子,这两个箱子(也可以是同一个)里的蛋糕颜色相同。
小杉现在想知道总共有多少种可爱的上色方案。
输入格式
一行一个整数n(1<=n<=25)
输出格式
仅有一行,一个整数,为上色方案数对19900801取模的结果
样例输入1
2
样例输出1
3

分析

注意性质2:”任意开启一个箱子,按里面蛋糕的颜色打开对应的箱子,这两个箱子(也可以是同一个)里的蛋糕颜色相同”。
我们借助一个图论模型来分析:将每个盒子看做一个点,颜色为i的盒子看成节点i,如果颜色为i的盒子中装了颜色为j的蛋糕,那么从i到j连一条有向边。显然,每个点的出度为1。
通过观察和分析发现,要满足性质2,如果i到j连了一条有向边(i!=j),那么从j出发的有向边必定连到j(自环)。即颜色为j的盒子里装的蛋糕颜色也为j。
因此,我们可以根据满足【蛋糕与装这个蛋糕的盒子颜色不同】的蛋糕数量x分类,那么该部分的答案为C(n,x)*(n-x)^x。容易发现,x不同时,方案是必定不会相同的。那么根据加法原理,我们从0到n-1枚举x,所得的各部分方案总和即为答案

然后我们发现19900801是个质数,所以求组合数直接算逆元递推就可以了

#include<cstdio>
const int mo=19900801;
int n;
long long ans,s,c,p;
long long pow(long long a,long long n){
    p=1;
    while (n){
        if (n&1)
            p=(p*a)%mo;
        a=(a*a)%mo;
        n>>=1;
    }
    return p;
}
int main(){
    scanf("%d",&n);
    if (n==1){
        printf("1\n");
        return 0;
    }
    ans=0;
    c=1;
    for (int i=0;i<n;i++){
        s=(c*pow(n-i,i))%mo;
        c=(c*(n-i))%mo;
        c=(c*pow(i+1,mo-2))%mo;
        ans=(ans+s)%mo;
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(组合数学,vijos)