您的位置:首页 > 其它

矩阵快速幂,动态规划(cjj's string game,HDU 5863)

2017-07-23 22:21 369 查看
一开始考虑后缀数组。

但有两个原因感觉不行。

1、没有一个给定的串让我来处理成后缀数组。

2、发现n很大,1e9,但m很小才10。时空完全无法承受。

然后考虑计算出来。

很容易想到让第一个串随便组合,然后尝试让第二个串满足它。

对于任意一个串,要求另一个串满足它的方案数都是一样多的。

满足的条件只关乎每个下标上的字符是否相等。显然和具体字符无关,只和字符的种数有关,所以方案数一样多。

一开始尝试组合数学。

关于n个数选m个数互不相邻的讲解,包括直线和圈上的:

https://www.zhihu.com/question/51429770/answer/125828470

当m=1的时候,理论上还可以算。用二项式定理可以O(logn)计算。

但是当m更大的时候,我们需要计算,n个数选a段不相邻的数,段长最多为b的方案数,理论上可以动态规划。

但是a是O(n)级别的,状态定义和转移时空都会爆。而且还要动规O(n)次。感觉不是很靠谱。

后来考虑计数DP

只需要计算一个串满足另一个固定串的方案数有多少。

满足就是说连续相等的串的长度至多为m,且至少有1个。

m才10,很快定义了一个状态并找到了转移。

dp[i][j][k]表示长度为i的串,最后j个字符相等,k表示是否至少有1个长度为m的串。

转移也是很简单,就是直接各种情况转移一下就好了。

但问题是n很大,有O(1e9)。可以考虑用矩阵快速幂来DP。

很多线性的DP都可以用矩阵快速幂来计算,最经典的就是斐波那契数列。

本题也是一样,我们直接把dp[0]的状态放到一个1行的矩阵里面,然后构造转移矩阵A,然后矩阵快速幂,A^n乘以这个初始矩阵就是dp
的状态。然后从dp{n]中取出你想要的值就好了。

当然我们没有必要真的构造出那个1行的矩阵。自己假想它和A^n相乘,然后脑补,直接从A^n这个矩阵中取走你想要的值就好了。

看了网上别人的解法,DP出最多为m的所有方案数。

然后DP[m]-DP[m-1]就是答案。

这个矩阵简单点,速度会快。

代码

#include<stdio.h>
using namespace std;
typedef long long ll;
const int mod = 1000000007;

struct Mat
{
int N;
int A[25][25];
void mem()
{
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
A[i][j]=0;
}
void I()
{
for(int i=0;i<N;i++)
A[i][i]=1;
}
void init(int m,int k)
{
N=2*(m+1);
mem();
for(int i=0;i<m;i++) A[i][0]=k-1;
for(int i=0;i<N-1;i++)
{
int j=i+1;
if(i==m) continue;
if(j==m) continue;
A[i][j]=1;
}
for(int i=m+1;i<N;i++)
A[i][m+1]=k-1;
A[m-1][N-1]=1;
}
Mat operator * (const Mat& rhs) const
{
Mat ret;
ret.N=N;
ret.mem();
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
for(int k=0;k<N;k++)
ret.A[i][j]=(ret.A[i][j]+1ll*A[i][k]*rhs.A[k][j]%mod)%mod;
return ret;
}
Mat operator ^ (int n)
{
Mat ret;
ret.N=N;
ret.mem();
ret.I();
Mat x;
x=*this;
while(n)
{
if(n&1) ret=ret*x;
x=x*x;
n>>=1;
}
return ret;
}
int getans()
{
int ret=0;
for(int i=N/2;i<N;i++)
ret=(1ll*ret+A[0][i])%mod;
return ret;
}
}MAT;

int mp(int x,int n)
{
int ret=1;
while(n)
{
if(n&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
n>>=1;
}
return ret;
}

int n,m,k;

void solve()
{
scanf("%d %d %d",&n,&m,&k);
MAT.init(m,k);
MAT=MAT^n;
ll ans=1ll*mp(k,n)*MAT.getans()%mod;
printf("%lld\n",ans);
}

int main()
{
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: