题解 奶牛题 / [USACO20DEC] Spaceship P
折腾了半天矩阵,结果正解是 DP
发现操作一个位置会将低于这一位的位清空
那么对把最高操作位扔状态里
令 \(f(h, i, j)\) 为操作的最高位 \(\leqslant h\) 时从 \(i\) 到 \(j\) 的方案数,不考虑第一步和最后一步的限制
转移时枚举一个最高位是在哪个点操作的
大概长成
\[f(h, i, j)=\sum\limits_{(u, k), (k, v)\in e}f(h-1, i, u)*f(h-1, v, j)
\]
枚举 \(k\) 后 \(\sum\limits_{(u, k)\in e}f(h-1, i, u)\) 和后面那个是独立的,可以分开预处理
注意这里枚举的路径上中转点 \(k\) 需要可以等于 \(i, j\)
否则无法处理比如说从 \(i\) 直接转移到 \(j\) 的情况
我因为上面这个东西折腾了半下午
然后加入第一步和最后一步的限制:
对每个询问建一个虚点,转移的时候仅在 \(h\) 和 \(k\) 都满足限制时进行转移
复杂度 \(O(nm(n+q^2))\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 65
#define ll long long
//#define int long long
int n, m, q;
char mp[N][N];
const ll mod=1e9+7;
int bs[N], s[N], bt[N], t[N];
ll f[N][N<<1][N<<1], g[N<<1], h[N<<1];
signed main()
{
freopen("cow.in", "r", stdin);
freopen("cow.out", "w", stdout);
scanf("%d%d%d", &n, &m, &q);
for (int i=1; i<=n; ++i) {
scanf("%s", mp[i]+1);
for (int j=1; j<=n; ++j) mp[i][j]-='0';
}
for (int i=1; i<=q; ++i) scanf("%d%d%d%d", &bs[i], &s[i], &bt[i], &t[i]);
for (int tim=1; tim<=m; ++tim) {
for (int i=1; i<=n+q; ++i)
for (int j=1; j<=n+q; ++j)
f[tim][i][j]=(f[tim][i][j]+f[tim-1][i][j])%mod;
for (int k=1; k<=n; ++k) {
memset(g, 0, sizeof(g));
memset(h, 0, sizeof(h));
g[k]=h[k]=1;
for (int i=1; i<=q; ++i) g[n+i] = bs[i]==tim && s[i]==k;
for (int i=1; i<=q; ++i) h[n+i] = bt[i]==tim && t[i]==k;
for (int i=1; i<=n+q; ++i)
for (int u=1; u<=n; ++u) if (mp[u][k])
g[i]=(g[i]+f[tim-1][i][u])%mod;
for (int j=1; j<=n+q; ++j)
for (int v=1; v<=n; ++v) if (mp[k][v])
h[j]=(h[j]+f[tim-1][v][j])%mod;
for (int i=1; i<=n+q; ++i)
for (int j=1; j<=n+q; ++j)
f[tim][i][j]=(f[tim][i][j]+g[i]*h[j])%mod;
}
}
for (int i=1; i<=q; ++i) printf("%lld\n", f[m][n+i][n+i]);
return 0;
}