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 }

 

posted @ 2014-11-01 14:24  e0e1e  阅读(742)  评论(0编辑  收藏  举报