• 刷题记录:牛客NC19115选择颜色


    传送门:牛客

    题目描述:

    n个人排成一个环形,每个人要从c种颜色中选择一个。
    牛牛希望相邻的人选择的颜色是不同的
    问有多少种方案。
    输出方案数对10007取模的结果。
    人是有顺序的,环旋转同构算不同的方案。
    输入:
    1000000000 100
    输出:
    726
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这道题算是排列组合中的一道经典题,俗称"填色问题",这种题目一般就是问

    有m中颜色,给n个区域涂色,问有几种方法
    
    • 1

    这种题目有一个通式:An=(m-1)^n+(-1)^n*(m-1),可以帮助我们快速的解决这类问题

    下面提供通式的推理过程

    首先我们假设n个区域涂色的方法有An中
    假设刚开始的位置和最后的位置的颜色不同,此时我们的方法数就是上诉的An
    假设我们刚开始的位置和最后的位置的颜色相同,那么相当于此时两个位置可以合并成一个位置.,方法数即为A(n-1)
    并且这两种情况的和恰恰是我们假设环形是链状涂色的情况,即An+A(n-1)=m*(m-1)^(n-1)
    因为m是已知的,此时就相当于得到了一个数列的递推公式,当然此时我们有很多方法去解这个递推公式,下面介绍一种累加的方法
    (-1)^(n-1)*An+(-1)^(n-1)*A(n-1)=m*(1-m)^(n-1)
    (-1)^n*An-(-1)^(n-1)A(n-1)=-m*(1-m)^n-1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后我们直接累加就会得到一个累加的公式

    ( − 1 ) n (-1)^{n} (1)nAn-A2=-m* ∑ i = 3 n \sum_{i = 3}^n i=3n ( 1 − m ) i − 1 (1-m)^{i-1} (1m)i1

    然后问题来到了 ∑ i = 3 n \sum_{i = 3}^n i=3n ( 1 − m ) i − 1 (1-m)^{i-1} (1m)i1

    我们将问题抽象化,上诉问题等价于求出 ∑ i = 1 n \sum_{i = 1}^n i=1n x i x^{i} xi的求和

    我们假设S= ∑ i = 1 n \sum_{i = 1}^n i=1n x i x^{i} xi

    然后有 S x \frac{S}{x} xS= ∑ i = 1 n \sum_{i = 1}^n i=1n x i x^{i} xi/x

    然后S- S x \frac{S}{x} xS= ∑ i = 1 n \sum_{i = 1}^n i=1n x i x^{i} xi- ∑ i = 1 n \sum_{i = 1}^n i=1n x i − 1 x^{i-1} xi1

    即可求出S的关于x的一个式子

    通过以上方法我们就可以得到An=(m-1)^n+(-1)^n*(m-1)的一个通项了

    然后使用快速幂就可以快乐的解决这道题啦(建议使用long long)

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    typedef long long ll;
    #define inf 0x3f3f3f3f
    #define root 1,n,1
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    inline ll read() {
    	ll x=0,w=1;char ch=getchar();
    	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*w;
    }
    #define maxn 1000000
    int mod=10007;
    ll qpow(ll a,ll b) {
    	ll sum=1;
    	while(b) {
    		if(b&1) {
    			sum=sum*a%mod;
    		}
    		a=a*a%mod;
    		b>>=1;
    	}
    	return sum;
    }
    int main() {
    	ll n,c;n=read();c=read();
    	if(n&1) {
    		cout<<qpow((c-1),n)+1-c<<endl;
    	}else {
    		cout<<qpow((c-1),n)+c-1<<endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    矩阵链相乘(动态规划)
    前端创建对象和数组的方法
    jwt+redis实现登录认证
    软件工程概述----- Scrum敏捷开发
    qmake TEMPLATE subdirs
    python的前缀树(字典树)
    jupyter notebook在base和其他虚拟环境下打开异常的问题
    05-5.4.2 树、森林和二叉树的转换
    Linux的文件的修改时间(mtime)、访问时间(atime)和状态时间(ctime)
    Hive分区表和分桶表
  • 原文地址:https://blog.csdn.net/yingjiayu12/article/details/126706184