bzoj 3120 矩阵优化DP

我的第一道需要程序建矩阵的矩阵优化DP。

题目可以将不同的p分开处理。

对于p==0 || p==1 直接是0或1

对于p>1,就要DP了。这里以p==3为例:

设dp[i][s1][s2][r]为前i列,结尾为0的有s1行(0表示女生,1表示男生),结尾为01的有s2个,结尾为011的有n-s1-s2个,有r列全是1的方案数。

状态这么复杂,看起来一点也不能用矩阵优化,但我们可以将状态(s1,s2,r)hash成整数,然后建立状态之间的转移。

收获:

这种m超过10^7的一般都要用矩阵优化,如果状态很复杂,可以将复杂的状态(但一般不多)hash成整数,然后用计算机建立状态之间的转移,然后就可以用矩阵优化了。

  1 /**************************************************************
  2     Problem: 3120
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:24504 ms
  7     Memory:2616 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <cstring>
 12 #define M 1000000007
 13 #define maxs 200
 14
 15 typedef long long dint;
 16
 17 int n, p, q;
 18 dint m;
 19 int comb[10][10];
 20
 21 void init_comb() {
 22     for( int i=0; i<=9; i++ )
 23         for( int j=0; j<=i; j++ ) {
 24             if( j==0 || i==j )
 25                 comb[i][j]=1;
 26             else
 27                 comb[i][j] = (comb[i-1][j]+comb[i-1][j-1]);
 28         }
 29 }
 30 struct Matrix {
 31     int n;
 32     dint v[maxs][maxs];
 33     void init( int nn ) {
 34         n=nn;
 35         for( int i=0; i<n; i++ )
 36             for( int j=0; j<n; j++ )
 37                 v[i][j]=0;
 38     }
 39     void make_unit( int nn ) {
 40         n=nn;
 41         for( int i=0; i<n; i++ )
 42             for( int j=0; j<n; j++ )
 43                 v[i][j] = i==j;
 44     }
 45     Matrix operator*( const Matrix & b ) const {
 46         const Matrix & a = *this;
 47         Matrix rt;
 48         memset( &rt, 0, sizeof(rt) );
 49         rt.n = b.n;
 50         for( int k=0; k<n; k++ ) {
 51             for( int i=0; i<n; i++ ) {
 52                 if( a.v[i][k] ) {
 53                     for( int j=0; j<n; j++ ) {
 54                         if( b.v[k][j] ) {
 55                             rt.v[i][j] += (a.v[i][k]*b.v[k][j])%M;
 56                             if( rt.v[i][j]>=M ) rt.v[i][j]-=M;
 57                         }
 58                     }
 59                 }
 60             }
 61         }
 62         /*
 63         for( int i=0; i<n; i++ ) {
 64             for( int j=0; j<n; j++ ) {
 65                 rt.v[i][j] = 0;
 66                 for( int k=0; k<n; k++ ) {
 67                     rt.v[i][j] += (a.v[i][k]*b.v[k][j])%M;
 68                     if( rt.v[i][j]>=M )
 69                         rt.v[i][j]-=M;
 70                 }
 71             }
 72         }
 73         */
 74         return rt;
 75     }
 76 };
 77
 78 Matrix mpow( Matrix a, dint b ) {
 79     Matrix rt;
 80     for( rt.make_unit(a.n); b; b>>=1,a=a*a )
 81         if( b&1 ) rt=(rt*a);
 82     return rt;
 83 }
 84 namespace Sec1 {
 85     void sov(){
 86         if( p==0 ) printf( "0\n" );
 87         else if( p==1 ) printf( "1\n" );
 88     }
 89 }
 90 namespace Sec2 {
 91     int info[maxs][3], idx[9][4], tot;
 92     Matrix trans;
 93     void init() {
 94         for( int r=0; r<=q; r++ )
 95             for( int a=0; a<=n; a++ ) {
 96                 info[tot][0] = a;
 97                 info[tot][1] = n-a;
 98                 info[tot][2] = r;
 99                 idx[a][r] = tot;
100                 tot++;
101             }
102         trans.init(tot);
103         for( int s=0; s<tot; s++ ) {
104             int a=info[s][0], r=info[s][2];
105             for( int sa=0; sa<=a; sa++ ) {
106                 int na=n-sa, nr=r+(sa==n);
107                 if( nr>q ) continue;
108                 int ns=idx[na][nr];
109                 trans.v[s][ns] = comb[a][sa];
110             }
111         }
112     }
113     void sov() {
114         init();
115         trans = mpow( trans, m );
116         int row = idx[n][0];
117         dint ans=0;
118         for( int i=0; i<tot; i++ ) {
119             ans += trans.v[row][i];
120             if( ans>=M ) ans-=M;
121         }
122         printf( "%lld\n", ans );
123     }
124 }
125 namespace Sec3 {
126     int info[maxs][4], idx[9][9][4], tot;
127     Matrix trans;
128     void init() {
129         for( int r=0; r<=q; r++ )
130             for( int a=0; a<=n; a++ )
131                 for( int b=0; b<=n-a; b++ ) {
132                     info[tot][0] = a;
133                     info[tot][1] = b;
134                     info[tot][2] = n-a-b;
135                     info[tot][3] = r;
136                     idx[a][b][r] = tot;
137                     tot++;
138                 }
139         trans.n = tot;
140         for( int s=0; s<tot; s++ ) {
141             int a=info[s][0], b=info[s][1], c=info[s][2], r=info[s][3];
142             for( int sa=0; sa<=a; sa++ )
143                 for( int sb=0; sb<=b; sb++ ) {
144                     int na=c+a-sa+b-sb, nb=sa, nr=r+(sa+sb==n);
145                     if( nr>q ) continue;
146                     int ns = idx[na][nb][nr];
147                     trans.v[s][ns] = comb[a][sa]*comb[b][sb];
148                 }
149         }
150     }
151     void sov(){
152         init();
153         trans = mpow( trans, m );
154         int row = idx[n][0][0];
155         dint ans=0;
156         for( int i=0; i<tot; i++ ) {
157             ans += trans.v[row][i];
158             if( ans>=M ) ans-=M;
159         }
160         printf( "%lld\n", ans );
161     }
162 }
163
164 int main() {
165     scanf( "%d%lld%d%d", &n, &m, &p, &q );
166     init_comb();
167     if( p<=1 ) Sec1::sov();
168     else if( p==2 ) Sec2::sov();
169     else if( p==3 ) Sec3::sov();
170 }

时间: 2024-10-06 11:06:25

bzoj 3120 矩阵优化DP的相关文章

bzoj 3437 斜率优化DP

写题解之前首先要感谢妹子. 比较容易的斜率DP,设sum[i]=Σb[j],sum_[i]=Σb[j]*j,w[i]为第i个建立,前i个的代价. 那么就可以转移了. 备注:还是要感谢妹子. /************************************************************** Problem: 3437 User: BLADEVIL Language: C++ Result: Accepted Time:3404 ms Memory:39872 kb **

Codeforces Round #341 (Div. 2) E. Wet Shark and Blocks(矩阵优化DP)

题目链接:点击打开链接 题意:给n个数作为一个块,有b个块,从其中若干个中选择数,每个块只能选一个数,最后组成一个数模x等于k的方法数. 思路:很容易想到这样一个DP方程 : 用dp[i][j]表示现在i位,余数是j.那么dp[i + 1][(j * 10 + k) % x] = dp[i][j] * cnt[k],k是指枚举放1~9中哪一位. 因为b特别大,显然要矩阵优化,知道了DP方程,其实矩阵的构造特别简单. 根据矩阵相乘的规则, 其实这个矩阵就是A[j][(j*10+a[k])%x]++

BZOJ1297 [SCOI2009]迷路 【矩阵优化dp】

题目 windy在有向图中迷路了. 该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1. 现在给出该有向图,你能告诉windy总共有多少种不同的路径吗? 注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间. 输入格式 第一行包含两个整数,N T. 接下来有 N 行,每行一个长度为 N 的字符串. 第i行第j列为'0'表示从节点i到节点j没有边. 为'1'到'9'表示从节点i到节点j需要耗费的时间. 输出格式 包含一个整数,可能的路径数

bzoj 1010 斜率优化DP

我的第二道斜率DP. 收获: 1.假设两个位置:p<q<i,然后让某一位置优,看其满足什么性质,所谓斜率优化就是满足: (g[q]-g[p])/(f[q]-f[p])  op h[i] 要化简成这样,必须满足f函数关于位置单调,否则op(<或>)的方向就会因为f的大小关系而变化,就没有凸的性质了. 2.斜率优化很难调试,所以当发现暴力DP和同样的方程被斜率优化了一下的答案不同时,不要去调试,直接去检查上面的各个函数是否写错或抄到代码中抄错了, 或者重推一遍.(注意决策点是否可能会重

Codeforces 351C Jeff and Brackets 矩阵优化DP

题意:你要在纸上画一个长度为n * m的括号序列,第i个位置画左括号的花费是a[i % n], 画右括号的花费是b[i % n],问画完这个括号序列的最小花费.n <= 20, m <= 1e7 思路:如果不管n和m的限制,这个题很好做,设dp[i][j]是到i位置,平衡因子是j的花费,dp[i][j] = min(dp[i - 1][j - 1] + a[i], dp[i - 1][j + 1] + b[i]),但是这样n * m到2e8级别,这是我们无法承受的.不过,我们可以发现一个性质:

bzoj1009 GT考试 (kmp+矩阵优化dp)

设f[i][j]是到第i位 已经匹配上了j位的状态数 然后通过枚举下一位放0~9,可以用kmp处理出一个转移的矩阵 然后就可以矩阵快速幂了 1 #include<bits/stdc++.h> 2 #define pa pair<int,int> 3 #define CLR(a,x) memset(a,x,sizeof(a)) 4 using namespace std; 5 typedef long long ll; 6 const int maxm=22; 7 8 inline

「模拟8.21」山洞(矩阵优化DP)

暴力: 正解: 考虑循环矩阵,f[i][j]表示从i点到j点的方案数 我们发现n很小,我们预处理出n次的f[i][j] 然后在矩阵快速幂中,我们要从当前的f[i][j]*f[j][k]-->fir[i][j] 但是此时的循环为三层 我们考虑转移式子的意义在0-n次从i-j,在n+1到2×n转移至j 这样此时的j-k其实可以把他看作从0开始走j-k步本质上是一样的 然后还有一个特判,就不讲了 for(int j=0;j<n;++j) { ff[now][j]=(ff[now][j]+ff[las

poj3734 Blocks[矩阵优化dp or 组合数学]

Blocks Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6578   Accepted: 3171 Description Panda has received an assignment of painting a line of blocks. Since Panda is such an intelligent boy, he starts to think of a math problem of paint

形态形成场(矩阵乘法优化dp)

形态形成场(矩阵乘法优化dp) 短信中将会涉及前\(k\)种大写字母,每个大写字母都有一个对应的替换式\(Si\),替换式中只会出现大写字母和数字,比如\(A→BB,B→CC0,C→123\),代表 \(A=12312301231230,B=1231230,C=123\).现在对于给定的替换式,求字符 AA 所代表的串有多少子串满足: 这个子串为单个字符\(0\)或没有前导\(0\). 把这个子串看作一个十进制数后模\(n\)等于\(0\). 答案对\(r\)取模.对于100%的数据,$2 \l