CodeForces 331E2 Deja Vu

发布时间 2023-12-22 16:51:33作者: zltzlt

洛谷传送门

CF 传送门

考虑一条好的路径 \(x \to y\) 中一定至少存在一条边 \((u, v)\),满足这条边的序列 \(a\) 存在一个 \(j \in [1, |a| - 1]\),满足 \(a_j = u, a_{j + 1} = v\),就是说 \(a\) 包含一对相邻的 \((u, v)\)

那么我们可以枚举这样的边 \(u \to v\),将这条边的序列 \(a\) 分成 \([\ldots, u], [v, \ldots]\) 的部分,然后两端独立地分别扩展,直到当前序列长度等于已经经过的点数。

找这样的一条路径复杂度 \(O(nm)\),可以通过 E1。

考虑我们上面的找法,实际上是找到了一些极小的好的路径(就是只存在一条边 \((u, v)\),满足边序列恰好包含一对相邻的 \((u, v)\))。我们希望用一些这样的路径拼出其他的好的路径。

我们可以在两条好的路径中间用一条边序列为空的边拼起来,或者在一条好的路径后面接一条“边序列在开头加上起点等于经过的点序列”的路径,或者在前面接一条“边序列在末尾加上终点等于经过的点序列”的路径。

所以我们可以用一个形如 \((x, y, k, p = 0/1, q = 0/1)\) 的五元组表示一条 \(x \to y\) 的极小的路径,长度为 \(k\),边序列是否包含起点,边序列是否包含终点(都为 \(0\) 说明这是一条 \(x \to y\) 的边序列为空的边)。那么两个三元组能拼接起来当且仅当前者的终点是后者的起点且前者的 \(q\) 异或后者的 \(p\)\(1\)

所以现在就能做一个背包 dp 了,设 \(f_{u, i, 0/1}\) 表示当前走到 \(u\),构成的路径长度为 \(i\),边序列是否包含终点。按长度从小到大转移即可。

时间复杂度仍为 \(O(nm)\)

code
// Problem: E2. Deja Vu
// Contest: Codeforces - ABBYY Cup 3.0 - Finals (online version)
// URL: https://codeforces.com/problemset/problem/331/E2
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 55;
const int maxm = 1300;
const int mod = 1000000007;

int n, m, tot, f[maxn][maxn << 1][2], p[maxn][maxn];
vector<int> G[maxn];
struct edge {
	int u, v;
	vector<int> S;
} E[maxm];

struct node {
	int x, y, k, p, q;
	node(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0) : x(a), y(b), k(c), p(d), q(e) {}
} a[1000000];

inline void upd(int &x, int y) {
	((x += y) >= mod) ? (x -= mod) : 0;
}

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1, k, x; i <= m; ++i) {
		scanf("%d%d%d", &E[i].u, &E[i].v, &k);
		while (k--) {
			scanf("%d", &x);
			E[i].S.pb(x);
		}
		p[E[i].u][E[i].v] = i;
	}
	for (int i = 1; i <= m; ++i) {
		vector<int> S = E[i].S;
		if (S.empty()) {
			a[++tot] = node(E[i].u, E[i].v, 1, 0, 0);
			continue;
		}
		for (int j = 1; j < (int)S.size(); ++j) {
			if (S[j - 1] == E[i].u && S[j] == E[i].v) {
				vector<int> v1, v2;
				for (int k = j - 1; ~k; --k) {
					v1.pb(S[k]);
				}
				int u = S[j - 1], cnt = 1;
				bool fl = 1;
				while (cnt < (int)v1.size() && (int)v1.size() <= n * 2) {
					int d = p[v1[cnt]][u];
					if (!d) {
						fl = 0;
						break;
					}
					for (int j = (int)E[d].S.size() - 1; ~j; --j) {
						v1.pb(E[d].S[j]);
					}
					u = v1[cnt];
					++cnt;
				}
				if (!fl || (int)v1.size() > n * 2) {
					continue;
				}
				for (int k = j; k < (int)S.size(); ++k) {
					v2.pb(S[k]);
				}
				u = S[j];
				cnt = 1;
				while (cnt < (int)v2.size() && (int)v1.size() + (int)v2.size() - 1 <= n * 2) {
					int d = p[u][v2[cnt]];
					if (!d) {
						fl = 0;
						break;
					}
					for (int x : E[d].S) {
						v2.pb(x);
					}
					u = v2[cnt];
					++cnt;
				}
				if (!fl || (int)v1.size() + (int)v2.size() - 1 > n * 2) {
					continue;
				}
				a[++tot] = node(v1.back(), v2.back(), (int)v1.size() + (int)v2.size() - 1, 1, 1);
			}
		}
		if (S.back() == E[i].u) {
			vector<int> vc;
			for (int j = (int)S.size() - 1; ~j; --j) {
				vc.pb(S[j]);
			}
			int u = S.back(), cnt = 1;
			bool fl = 1;
			while (cnt < (int)vc.size() && (int)vc.size() <= n * 2) {
				int d = p[vc[cnt]][u];
				if (!d) {
					fl = 0;
					break;
				}
				for (int j = (int)E[d].S.size() - 1; ~j; --j) {
					vc.pb(E[d].S[j]);
				}
				u = vc[cnt];
				++cnt;
			}
			if (fl && (int)vc.size() - 1 <= n * 2) {
				a[++tot] = node(vc.back(), E[i].v, (int)vc.size(), 1, 0);
			}
		}
		if (S[0] == E[i].v) {
			vector<int> vc;
			for (int j = 0; j < (int)S.size(); ++j) {
				vc.pb(S[j]);
			}
			int u = S[0], cnt = 1;
			bool fl = 1;
			while (cnt < (int)vc.size() && (int)vc.size() <= n * 2) {
				int d = p[u][vc[cnt]];
				if (!d) {
					fl = 0;
					break;
				}
				for (int x : E[d].S) {
					vc.pb(x);
				}
				u = vc[cnt];
				++cnt;
			}
			if (fl && (int)vc.size() - 1 <= n * 2) {
				a[++tot] = node(E[i].u, vc.back(), (int)vc.size(), 0, 1);
			}
		}
	}
	for (int i = 1; i <= tot; ++i) {
		G[a[i].x].pb(i);
		if (a[i].p) {
			++f[a[i].y][a[i].k][a[i].q];
		}
	}
	for (int i = 1; i <= n * 2; ++i) {
		int ans = 0;
		for (int u = 1; u <= n; ++u) {
			upd(ans, f[u][i][1]);
			for (int o = 0; o <= 1; ++o) {
				if (!f[u][i][o]) {
					continue;
				}
				for (int j : G[u]) {
					if (i + a[j].k <= n * 2 && a[j].p == (o ^ 1)) {
						upd(f[a[j].y][i + a[j].k][a[j].q], f[u][i][o]);
					}
				}
			}
		}
		printf("%d\n", ans);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}