题解 奶牛题 / [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;
}
posted @ 2022-07-13 18:08  Administrator-09  阅读(2)  评论(0编辑  收藏  举报