题目如图
题意是说给n个花,m种颜色,要求相邻花颜色不能相同,总共有k种不同的颜色。
刚开始想的是从m种颜色里选k种,然后第一朵花有k种选法,后面的都是k-1种,这样就是C(m,k) * (k - 1) ^ (n - 1) * k,然后交的时候第二个一直wa,后来发现会这样并不能保证有k种颜色,比如4朵花3种颜色可以是1,2,1,2,只有两种颜色。所以后来的思路是从这个里面减去不超过k - 1种颜色的选法就是答案,然后想的式子是C(m,k)((k - 1) ^ (n - 1) * k - C(k,k - 1) * (k - 1) * (k - 2) ^ (n - 1)),这个意思是k种颜色里选k - 1种涂n朵花,第一朵是k - 1种涂法,后面都是k - 2种,所以就是 C(k,k - 1) * (k - 1) * (k - 2) ^ (n - 1),减去这种情况就是k种颜色的,后来发现这样也有问题。比如n = 4,m = 4,k = 4的时候,假设颜色是从1到4,从k = 4种颜色里选三种颜色会有1,2,3 , 1,2,4, 1,3,4 ,2,3,4这四种选法,其中在1,2,3这种情况会有1,2,1,2这种涂法,在1,2,4这种情况也会有1,2,1,2这种涂法,相当于把两种颜色的算了两遍。然后依次类推从五种颜色选四种会出现把三种颜色的算2遍,两种颜色的算三遍。记f[i] = C(k,i) * i * (i - 1) ^ (n - 1),那么不超过2种颜色的应该是f[2] - f[1],不超过三种颜色是f[3] - 不超过两种的,不超过四种是f[4] - 不超过三种的,所以不超过k - 1种就是f[k - 1] - 不超过k - 2种的,可以列出式子为(f[k - 1] - (f[k - 2] - (f[k - 3]… - (f[2] - f[1])))),然后由(k - 1) ^ (n - 1) * k减去它,也就是这种形式C(m,k) * (∑(i = 2,k) (-1) ^ (k - i) * f[i])。
在算的过程中需要用到逆元,因为求C(n,m)需要用到除法,所以用快速幂处理,a的逆元就是pow(a,mod - 2)。然后要对C(k,i)进行打表,求出i从1到k的值,还需要在最外层对逆元从1到1000000打表,不然会超时。
代码如下:
#include<stdio.h> #include<math.h> #include<set> using namespace std; typedef long long ll; const int mod = 1000000007; ll c[1000010],inverse[1000010]; ll quickPow(ll a,ll n){ if(n == 0) return 1; ll ans = quickPow(a,n / 2); ans=ans%mod; if(n % 2 == 0) return ans * ans % mod; return (((ans * ans) % mod) * a) % mod; } ll C(ll a,ll b){ ll ans=1; for(int i=1;i<=b;i++){ ans*=(a-i+1); ans%=mod; ans*=inverse[i]; ans%=mod; } return ans; } int main(){ int T; scanf("%d",&T); int t = 0; for(int i = 1;i <= 1000000;i++) inverse[i] = quickPow(i,mod - 2); while(T--){ t++; ll n,m,k; scanf("%lld%lld%lld",&n,&m,&k); c[1] = k; for(int i = 2;i <= k;i++){ c[i] = c[i - 1] * (k - i + 1) % mod; c[i] = c[i] * inverse[i] % mod; } ll ans = 0; int sign = pow(-1,k - 1); for(int i = 1;i <= k;i++){ ans = (ans + sign * i % mod * c[i] % mod * quickPow(i - 1, n - 1) % mod + mod) % mod; sign *= -1; } ans = ans * C(m,k) % mod; printf("Case #%d: %lld ",t,ans); } }