CF 1425B Blue and Red of Our Faculty!
容易发现这张图就是若干个相交于点 \(1\) 的环,我们先 \(dfs\) 把环找出来。
考虑最后什么情况会停下来:
- 外面有若干个环被走完,两个人在当前环内相遇。最后在同一个点或隔一条边均可。
- 走完了整个图。此时两个人均在 \(1\) 号点。
- 只有一条边没被走。此时一个人在 \(1\) 号点。
对于第一种情况,我们枚举相遇的环,设这个环的大小为 \(R\),两个人分别在外面走了 \(A,B\),那么两个人能够在这个环相遇的充要条件是:\(|A-B| \leq R - 2\)。否则一个人就会把这个环走完。我们再枚举一下 \(A\),那么可行的 \(B\) 就是一段区间,容易通过背包计算。
具体来说:我们设 \(f[i][j]\) 表示前 \(i\) 个环,\(A - B = j\) 的方案数,\(g[i][j]\) 表示后缀的背包。我们在统计答案的时候只需要对 \(g[i]\) 做一个前缀和即可。
对于第 \(2\) 种情况,我们直接对所有的环求一遍背包即可。
对于第 \(3\) 种情况,我们还是枚举最后剩下的那一条边在哪个环中,再枚举 \(A\),这样可行的 \(B\) 就确定了,直接合并前缀后缀两个背包即可。
需要注意的是,对于第 \(1\) 种情况,环是可以不选的,对于 \(2, 3\) 两种情况,环是必选的。对于 \(1, 3\) 两种情况,答案还需要 \(\times2\),因为不能确定 \(A\) 从哪个方向进去,而对于第 \(2\) 种情况,方向是无所谓的。
#include <bits/stdc++.h>
const int N = 8e3 + 20, Mod = 1e9 + 7;
#define tr(i) (i + 4005)
inline int read()
{
int x = 0;
char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x;
}
int tot = 1, fir[N], nex[N], got[N];
inline void AddEdge(int u, int v)
{
nex[++tot] = fir[u], fir[u] = tot, got[tot] = v;
}
int vis[N], a[N];
inline int dfs(int u)
{
vis[u] = true;
for (int i = fir[u]; i; i = nex[i])
if (!vis[got[i]]) return dfs(got[i]) + 1;
return 0;
}
int f[2][N], g[N >> 1][N];
int main()
{
int n = read(), M = read(), m = 0; vis[1] = true;
for (int i = 1; i <= M; i++)
{
int u = read(), v = read();
AddEdge(u, v), AddEdge(v, u);
}
for (int i = fir[1]; i; i = nex[i])
if (!vis[got[i]]) a[++m] = dfs(got[i]) + 2;
f[0][tr(0)] = g[m + 1][tr(0)] = 1;
int now = 0;
for (int i = m; i >= 1; --i) for (int j = -M; j <= M; ++j)
{
g[i][tr(j)] = g[i + 1][tr(j)];
if (j - a[i] >= -M) (g[i][tr(j)] += g[i + 1][tr(j - a[i])]) %= Mod;
if (j + a[i] <= M) (g[i][tr(j)] += g[i + 1][tr(j + a[i])]) %= Mod;
}
int ans = 0;
for (int i = 1; i <= m; ++i)
{
int w = a[i] - 2, res = 0;
for (int j = -M; j <= M; ++j) (g[i + 1][tr(j)] += g[i + 1][tr(j - 1)]) %= Mod;
for (int j = -M; j <= M; ++j) if (f[now][tr(j)])
{
int l = std::max(j - w, -M), r = std::min(j + w, M);
(res += 2LL * (g[i + 1][tr(r)] - g[i + 1][tr(l - 1)] + Mod) * f[now][tr(j)] % Mod) %= Mod;
}
(ans += res) %= Mod, now ^= 1;
for (int j = -M; j <= M; ++j)
{
f[now][tr(j)] = f[now ^ 1][tr(j)];
if (j - a[i] >= -M) (f[now][tr(j)] += f[now ^ 1][tr(j - a[i])]) %= Mod;
if (j + a[i] <= M) (f[now][tr(j)] += f[now ^ 1][tr(j + a[i])]) %= Mod;
}
}
memset(g, 0, sizeof(g)), g[0][tr(0)] = 1;
for (int i = 1; i <= m; ++i) for (int j = -M; j <= M; ++j)
{
if (j - a[i] >= -M) (g[i][tr(j)] += g[i - 1][tr(j - a[i])]) %= Mod;
if (j + a[i] <= M) (g[i][tr(j)] += g[i - 1][tr(j + a[i])]) %= Mod;
}
(ans += g[m][tr(0)]) %= Mod;
memset(f, 0, sizeof(f)), f[0][tr(0)] = 1, now = 0;
for (int i = m; i >= 1; --i)
{
for (int j = -M; j <= M; ++j)
{
if (!f[now][tr(j)]) continue;
int w1 = - (a[i] - 1) - j, w2 = (a[i] - 1) - j;
if (w1 >= -M) (ans += 2LL * f[now][tr(j)] * g[i - 1][tr(w1)] % Mod) %= Mod;
if (w2 <= M) (ans += 2LL * f[now][tr(j)] * g[i - 1][tr(w2)] % Mod) %= Mod;
}
now ^= 1;
for (int j = -M; j <= M; ++j)
{
f[now][tr(j)] = 0;
if (j - a[i] >= -M) (f[now][tr(j)] += f[now ^ 1][tr(j - a[i])]) %= Mod;
if (j + a[i] <= M) (f[now][tr(j)] += f[now ^ 1][tr(j + a[i])]) %= Mod;
}
}
std::cout << ans << std::endl;
return 0;
}

浙公网安备 33010602011771号