题目大意给你一个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的时间内完成。
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 }