题目链接:https://ac.nowcoder.com/acm/contest/19483/E
题目一上手,很容易想到动态规划。用dp[i][0]表示从第1层到达第i层左边的方案数,dp[i][1]表示从第1层到达第i层右边的方案数:
当梯子为'/'时,
dp[i][1]=dp[i-1][0]+dp[i-1][1]
dp[i][0]=dp[i-1][0]当梯子为'\'时
dp[i][1]=dp[i-1][1]
dp[i][0]=dp[i-1][0]+dp[i-1][1]初值dp[1][0]=dp[1][1]=1
这时可以发现,该转移方程可写成矩阵形式
为了表示初位置是左还是右,我们采用2 * 2的矩阵来表示。
其中t是从第i-1层到第i层的转移矩阵。
当梯子为'/'时,
当梯子为'\'时,
这样就能记录从第1层到第i层四种方式(左到左、左到右、右到左、右到右)的方法数啦
注意:
1.在计算hs到ht的方法数时,需要用1到ht的方法减去1到hs的方法,即presum[ht]/presum[hs](注意不是hs-1)。这里需要把除变成乘,通过求逆矩阵实现。
2.矩阵乘法不满*** 换 律。由于 1到ht方法presum[ht]= 1到hs方法presum[hs] *hs到ht的一堆转移矩阵t ,因此presum[hs]的逆需要乘在前面,才能把presum[hs]抵消掉
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
const ll mod=1e9+7;
const int n=2;//转换矩阵的尺寸为2*2
ll qsm(ll a,ll b){//快速幂
ll t=a,ans=1;
while(b){
if(b&1) ans=ans*t%mod;
t=t*t%mod;
b>>=1;
}
return ans%mod;
}
ll ni(int x){//求逆元
return qsm((ll)x,mod-2);
}
struct Matrix{
static const int N=10;
ll a[N][N];
Matrix(ll e=0){//新建一个matrix变量时,自动初始化为全0矩阵
for (int i=0;i<=n-1;i++)for (int j=0;j<=n-1;j++)a[i][j]=e*(i==j);
}
Matrix(ll a1,ll a2,ll a3,ll a4){
a[0][0]=a1; a[0][1]=a2;
a[1][0]=a3; a[1][1]=a4;
}
}presum[100010];//当需要求矩阵的逆时,其尺寸为[n][n<<1]
Matrix mul(Matrix A,Matrix B){
Matrix ans(0);//构造全0矩阵
for (int i=0;i<=n-1;i++){
for (int j=0;j<=n-1;j++){
for (int k=0;k<=n-1;k++){
ans.a[i][j]=(ans.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
}
}
}
return ans;
}
Matrix ksm(Matrix A,ll b){
Matrix ans(1);//构造单位阵
while (b){
if (b&1)ans=mul(ans,A);
A=mul(A,A);b>>=1;
}
return ans;
}
void row_minus(Matrix &A,int a,int b,ll k){//矩阵A的第a行减k*第b行
_for(i,0,2*n-1){
A.a[a][i]=(A.a[a][i]-A.a[b][i]*k%mod+mod)%mod;
}
}
void row_multiplies(Matrix &A,int a,ll k){//第a行乘k
_for(i,0,2*n-1){
A.a[a][i]=(A.a[a][i]*k)%mod;
}
}
void row_swap(Matrix &A,int a,int b){//交换a和b两行
_for(i,0,2*n-1){
swap(A.a[a][i], A.a[b][i]);
}
}
Matrix getinv(Matrix x){//求矩阵x的逆
Matrix A(0);
_for(i,0,n-1){
_for(j,0,n-1){
A.a[i][j]=x.a[i][j];
A.a[i][n+j]=(i==j);
}
}
_for(i,0,n-1){
if(!A.a[i][i]){
_for(j,i+1,n-1){
if(A.a[j][i]){
row_swap(A,i, j);
break;
}
}
}
row_multiplies(A,i,ni(A.a[i][i]));
_for(j,i+1,n-1){
row_minus(A,j, i, A.a[j][i]);
}
}
_rep(i,n-1,0){
_rep(j,i-1,0){
row_minus(A,j, i, A.a[j][i]);
}
}
Matrix ret(0);
_for(i,0,n-1){
_for(j,0,n-1){
ret.a[i][j]=A.a[i][n+j];
}
}
return ret;
}
const Matrix tA(1,1,0,1);
const Matrix tB(1,0,1,1);
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int h,q;
cin >> h >> q;
//presum[0]=Matrix(1,0,0,1);
presum[1]=Matrix(1,0,0,1);
Matrix ans=getinv(presum[0]);
_for(i,2,h){
char c;
cin >> c;
if(c=='/'){
presum[i]=mul(presum[i-1],tA);
}
else if(c=='\\'){//注意打两个右斜杠
presum[i]=mul(presum[i-1],tB);
}
}
while(q--){
int hs,ht,ps,pt;
cin >> hs >> ht >> ps >> pt;
Matrix ans=mul(getinv(presum[hs]),presum[ht]);//注意顺序不可颠倒
cout << ans.a[ps][pt] << "\n";
}
return 0;
}
京公网安备 11010502036488号