The 2014 ACM-ICPC Asia Xi'an Regional Contest — F题 Color
觉得这题真要总结一下,先贴一个半AC的代码(还有组合数超时问题)。
1)西安赛场上我们推出了一个dp方程:
定义前i个位置使用了j种颜色的花(一共备选m种):
边界是F(1,1) = m,答案是F(n, k)。很显然直接递推的方式是O(nk)的复杂度,显然受不了。然后我们很sb的在赛场上搞各种矩阵快速幂压缩方法,106的矩阵,最后这个dp方程成功铸铁。
2)不这样想应该怎么思考呢?如果我们换一个角度,让N个位置固定,
凭直觉我们能想出C(m, k) * k * (k-1)n-1这么一个式子,对不对呢?显然它是能保证相邻两朵花不同颜色了,但并不能保证刚好用k朵花(例如有花1、2、3,K=3,N=3,那么121也被计算在内了)。
难道做不通了么?。。。如果将F[i, j]定义为在i朵备选花中,选j朵的方案数:
这是一个方法,因为它的复杂度终于跟M和N无关了,我们对它进行化简和二项式反演:
期望时间O(K2),还是不行。
3)在群里问,并且验证过的,最终的解法是:
F[k] = C(m, k) * k * (k-1)n-1-F[k-1]
ans = F[k]
期望时间O(K),这个需要证明
。。然后我证不出来,然后然后就没有然后了。。
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 5 typedef long long LL; 6 const int mod = 1e9+7; 7 8 int pow_mod(int p, int k) 9 { 10 LL ans = 1; 11 while(k) { 12 if (k & 1) ans = ans * p % mod; 13 p = (LL)p*p % mod; 14 k >>= 1; 15 } 16 return ans; 17 } 18 19 //以下为求逆元 20 ///*************// 21 LL extend_gcd(LL a,LL b,LL &x,LL &y) 22 { 23 if(b==0) 24 { 25 x=1; 26 y=0; 27 return a; 28 } 29 LL gcd=extend_gcd(b,a%b,x,y); 30 LL t=x; 31 x=y; 32 y=t-a/b*x; 33 return gcd; 34 } 35 LL Get_Inverse(LL num) 36 { 37 LL x,y; 38 extend_gcd(num,mod,x,y); 39 return (x%mod+mod)%mod; 40 } 41 ///*************// 42 LL comb(LL n,LL m)//计算组合数C(n,m) 43 { 44 LL t1=1,t2=1; 45 for(LL i=n;i>m;i--) 46 { 47 t1=(t1*i)%mod; 48 t2=(t2*(i-m))%mod; 49 } 50 return t1*Get_Inverse(t2)%mod; 51 } 52 53 54 /* 55 int dp[10007][10007]; 56 57 LL dfs(int N, int m, int k) { 58 if (~dp[m][k]) return dp[m][k]; 59 LL ans = 0; 60 for(int j = 1; j<k; ++j) 61 ans = (ans + dfs(N, k,j)) % mod; 62 return dp[m][k] = (comb(m, k)*(((LL)k*pow_mod(k-1, N-1) % mod - ans) % mod)) % mod; 63 }*/ 64 65 int k_kdec[100007]; 66 67 int main(void) 68 { 69 int T; 70 scanf("%d", &T); 71 72 for(int t=1; t<=T; ++t) { 73 int N, M, K; 74 scanf("%d%d%d", &N, &M, &K); 75 // memset(dp, -1, sizeof dp); 76 // 暴力 77 // printf("%d\n", dfs(N, M, K)); 78 /* 79 for(int m=1; m<=K; ++m) { 80 k_kdec[m] = 0; 81 for(int k=1; k<m; ++k) 82 k_kdec[m] = (k_kdec[m] + (comb(m, k) * ((LL)k*pow_mod(k-1, N-1) % mod)) % mod) % mod; 83 } 84 // 二项式反演 85 int sgn[] = {1, -1}; 86 int bk = 0; 87 for(int k = 1; k<=K; ++k) 88 bk = (bk +(sgn[(K-k) & 1] * (comb(K, k) * k_kdec[k]) % mod)) % mod; 89 printf("%I64d\n", (comb(M, K) * (((LL)K*pow_mod(K-1, N-1) % mod) - bk) % mod) % mod);*/ 90 91 // 容斥直接搞 92 LL ans = 0; 93 int sgn = 1; 94 for(int k = K; k>=1; --k) { 95 ans = (ans + sgn * ((comb(M, k) * ((LL)k*pow_mod(k-1, N-1) % mod)) % mod)) % mod; 96 sgn = -sgn; 97 } 98 printf("Case #%d: %d\n", t, (int)ans); 99 } 100 }
This article is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。