「USACO 2020.12 Platinum」Spaceship
看到题目第一个想到的是:按照路径长度可以确定按钮次数和路径次数
然而路径长度是(2^k)级别的。。
下文认为(n,q,k)同阶
既然无法考虑长度,那么就直接在(dp)时将路径作为状态压入
令(dp_{i,s,t})表示前面(i)个按钮未按过,从(s)走到(t)的路径数
显然(dp_{i})可以看做一个类似矩阵的转移,设邻接矩阵为(E)
那么得到(dp_{i}=dp_{i-1}+dp_{i-1}cdot Ecdot dp_{i-1})
((dp_i)不一定按了(i)这个按钮,所以考虑按或者不按)
那么对于(b_s,b_t)的情况,考虑把操作序列分成两段
显然操作序列有唯一一个按过恰好一次的最大的按钮(max),可以在这里将操作序列分成两段
处理出(b_s)到(max)和(max)到(b_t)的两部分方案数合并即可
以处理(b_s ightarrow max)为例
类似上面的操作,令(F_{i,j})表示最大按钮为(i),且按过(i)之后没有按过其他按钮,停留在(j)的方案数 ((F_i)是一个向量)
初始显然有(F_{b_s,s}=1)
(displaystyle i>b_s,F_{i}=sum_{j<i} F_{j}cdot dp_{j-1}cdot E)(按过(j)之后可以继续按([1,j-1]))
前缀和即可,复杂度为(O(n^3))
同理处理出(max ightarrow b_t)的权值合并即可
#include<bits/stdc++.h>
using namespace std;
#define Mod1(x) ((x>=P)&&(x-=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
const int N=62,P=1e9+7,INF=1e9+10;
int n,m,q;
struct Mat{
int a[N][N];
void clear(){ memset(a,0,sizeof a); }
Mat operator * (const Mat &x) const {
Mat res; res.clear();
rep(i,1,n) rep(j,1,n) if(a[i][j]) rep(k,1,n) res.a[i][k]=(res.a[i][k]+1ll*a[i][j]*x.a[j][k])%P;
return res;
}
Mat operator + (const Mat &x) const {
Mat res;
rep(i,1,n) rep(j,1,n) res.a[i][j]=a[i][j]+x.a[i][j],Mod1(res.a[i][j]);
return res;
}
} I,E,dp[N];
int F[N][N],G[N][N];
int S[N][N];
int main(){
freopen("spaceship.in","r",stdin),freopen("spaceship.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
rep(i,1,n) I.a[i][i]=1;
rep(i,1,n) rep(j,1,n) scanf("%1d",&E.a[i][j]);
dp[0]=I;
rep(i,1,m) dp[i]=dp[i-1]+dp[i-1]*E*dp[i-1];
while(q--) {
int bs,s,bt,t; scanf("%d%d%d%d",&bs,&s,&bt,&t);
memset(F,0,sizeof F),memset(G,0,sizeof G);
memset(S,0,sizeof S);
F[bs][s]=1;
rep(i,1,n) S[bs][i]=dp[bs-1].a[s][i];
rep(i,bs+1,m) {
rep(a,1,n) if(S[i-1][a]) rep(b,1,n) F[i][b]=(F[i][b]+1ll*S[i-1][a]*E.a[a][b])%P;
rep(a,1,n) S[i][a]=S[i-1][a];
rep(a,1,n) if(F[i][a]) rep(b,1,n) S[i][b]=(S[i][b]+1ll*F[i][a]*dp[i-1].a[a][b])%P;
}
memset(S,0,sizeof S);
G[bt][t]=1;
rep(i,1,n) S[bt][i]=dp[bt-1].a[i][t];
rep(i,bt+1,m) {
rep(a,1,n) rep(b,1,n) G[i][a]=(G[i][a]+1ll*S[i-1][b]*E.a[a][b])%P;
rep(a,1,n) S[i][a]=S[i-1][a];
rep(a,1,n) rep(b,1,n) S[i][a]=(S[i][a]+1ll*G[i][b]*dp[i-1].a[a][b])%P;
}
int ans=0;
rep(i,1,m) rep(j,1,n) ans=(ans+1ll*F[i][j]*G[i][j])%P;
printf("%d
",ans);
}
}