【DP+压缩矩阵+矩阵乘法】阅读

来源:互联网 发布:mac 创建.gitignore 编辑:程序博客网 时间:2024/04/29 19:25

【湖南集训】阅读 ——By hta


【问题描述】

有趣的事是实,汉字的序顺并不定一能影阅响读,比如当你看完这句话后,才发这现里
的字全是都乱的。
其实英语也是如此:The maening of a sentenec with almspt no corrcet word can be
undrestood esaily.
现在小G发现了这个神奇的事实,于是他给一些字母对定义了一个差异值F(1<=F<=5),
相似的字母差异值小,差别大的字母差异值则大。这样,每个单词都可以被赋予一个权值,
即为相邻字母间的差异值之和。权值小的单词,可读性就强,即使有字母顺序的错乱,也可
以容易理解它的意思。
比如定义(a,p),(p,l),(l,e),(p,p)的差异值分别为2,3,4,1,则apple 的权值就是2+1+3+4=10。

现在,小G 想统计共有多少权值不超过N 的不同的单词。

N <= 10000000


【题解】

很容易想到一个DP,f[i][j]表示权值为i时以字母j结尾有多少钟不同的方案。

f[i][j] = Σf[i-w[i][k]][k];

这算法时间复杂度为26*26*N,对于N<=10000000是无法承受的。


对于这种没有条件转移的DP方程,我们很容易想到用矩阵来优化速度。

可是本题DP方程一眼看去转移时i和j的都在变化,好像很难用矩阵优化。

但是经过仔细阅读题目,我们发现w[i][k] <= 5,这就启发我们将二维转移压缩成一维的。

对于f[i][j],我们只可由f[i-1][k],f[i-2][k],f[i-3][k],f[i-4][k],f[i-5][k]转移得到,所以我们把f[i-5],f[i-4],f[i-3],f[i-2],f[i-1]压成一个5*26的一位矩阵。

则我们的转移变成由f[i-5],f[i-4],f[i-3],f[i-2],f[i-1] 转移到 f[i-4],f[i-3],f[i-2],f[i-1],f[i]。

转移矩阵是(5*26)*(5*26)的,可以由w[i][k]构造。

因为题目说要求权值不超过N的,所以另外我们还需添加一个终结节点,把所有字母向它连,它自己也向自己连。

(具体细节看代码)。

另外做这种较大的矩阵乘法时要注意栈空间,应写非递归,而且要避免过多的中间变量,能开全局变量最好开全局的。


#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for (int i = a;i <= b;i ++)using namespace std;typedef long long LL;const int P = 1000000007;const int maxn = 1000005;int N,M,w[27][27],Ans;struct Matrix{int r,h;int x[132][132];}tr,ans,ret;inline Matrix operator*(const Matrix &a,const Matrix &b){memset(ret.x,0,sizeof ret.x);ret.r = a.r; ret.h = b.h;fo(i,1,ret.r)fo(j,1,ret.h)fo(k,1,a.h)ret.x[i][j] = (ret.x[i][j] + (LL)a.x[i][k] * b.x[k][j] % P) % P;return ret;}void POW(int exp){while (exp){if (exp & 1) ans = ans * tr;tr = tr * tr;exp >>= 1;} }void Initialize(){scanf("%d%d",&N,&M);fo(i,1,M){char c1,c2;do c1 = getchar(); while (c1 < 'a' || c1 > 'z');do c2 = getchar(); while (c2 < 'a' || c2 > 'z');scanf("%d",&w[c1-96][c2-96]);w[c2-96][c1-96] = w[c1-96][c2-96];}fo(i,1,26) fo(j,1,26)if (!w[i][j]) w[i][j] = 1;fo(i,105,130) ans.x[1][i] = 1;fo(i,1,26) fo(j,1,26)tr.x[105-26*(w[i][j]-1)+i-1][105+j-1] = 1;fo(i,27,130) tr.x[i][i-26] = 1;fo(i,105,130) tr.x[i][131] = 1;tr.x[131][131] = 1;ans.r = 1; ans.h = 131;tr.r = 131; tr.h = 131;}void Work(){POW(N);Ans = ans.x[1][131];fo(i,105,130) Ans = (Ans + ans.x[1][i]) % P;printf("%d\n",Ans);}int main(){freopen("reading.in","r",stdin);freopen("reading.out","w",stdout);Initialize();Work();return 0;}


0 0