CodeForces - 233D

题目大意给你一个n*m 的矩阵,要求你进行涂色,保证每个n*n的矩阵内都有k个点被涂色。

问你一共有多少种涂色方案。 n<=100 && m<=1e18

 

看数据范围感觉是个矩阵快速幂优化的dp,各种想,连状态转移方程都想不出来,我真

鸡儿菜!!!!,这种和概率有关的dp我感觉好蓝啊!!!

 

思路:显然是个dp,我们另dp[ i ][ j ]表示,到 i 列,一共涂了j个格子的种数,

那么有状态转移方程 dp[ i ][ j ] +=Σ(dp[ i - 1 ][ j - s ] * c[ n ][ s ] ) ( 0<=s<=j ) c[ i ] [ j ]表示组合数。

但是m的范围是1e18显然不能直接dp,我们观察可以发现,第 i 列 和 第 i + n 列的涂色格子的

个数肯定是一样的,那么我们可以用快速幂把>n的列的种数全部并到<n 的列中,这样就能在

100^3的时间内完成。

Codeforces Round #144 (Div. 2) D table_快速幂Codeforces Round #144 (Div. 2) D table_快速幂_02
 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const ll mod=1e9+7;
 5 ll c[101][101],n,m,k,dp[101][10001],res[101][10001][2];
 6 void init()
 7 {
 8     c[1][1]=1;
 9     for(int i=2;i<=100;i++)
10     {
11         c[i][1]=i;
12         c[i][i]=c[i][0]=1;
13         for(int j=2;j<i;j++)
14         {
15             c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
16         }
17     }
18 }
19 ll q_pow(ll x,ll k)
20 {
21     ll ans=1,d=x;
22     while(k)
23     {
24         if(k&1) ans=(ans*d)%mod;
25         d=(d*d)%mod;
26         k>>=1;
27     }
28     return ans;
29 }
30 int main()
31 {
32     init();
33     cin>>n>>m>>k;
34     int r=m%n;
35     for(int i=1;i<=n;i++)
36     {
37         for(int j=0;j<=k;j++)
38         {
39             if(j>n) break;
40             if(i<=r) res[i][j][0]=q_pow(c[n][j],m/n+1);
41             else res[i][j][1]=q_pow(c[n][j],m/n);
42         }
43     }
44     for(int i=0;i<=n;i++) dp[i][0]=1;
45     for(int i=1;i<=n;i++)
46     {
47         for(int j=1;j<=k;j++)
48         {
49             for(int u=0;u<=min((ll)j,n);u++)
50             {
51                 if(i<=r) dp[i][j]=(dp[i][j]+dp[i-1][j-u]*res[i][u][0])%mod;
52                 else dp[i][j]=(dp[i][j]+dp[i-1][j-u]*res[i][u][1])%mod;
53             }
54         }
55     }
56     printf("%I64d\n",dp[n][k]);
57     return 0;
58 }
View Code