题解:CF2183F Jumping Man
这里介绍不用转成 dfn 的做法。
有一个经典的转化就是你求次数的平方和等价于选择两个字符串相等的方案数。
显然是要动态规划,我们设
首先有显然的转移。
考虑怎么优化,我们可以考虑下实际意义,就是你要从
还有
显然有:
有了
注意转移顺序按照后序遍历即可。
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i ++ )
#define repf(i, a, b) for(int i = (a); i >= (b); i -- )
typedef long long ll;
using namespace std;
const int N = 5000 + 10;
const int P = 998244353;
int n; char s[N];
int head[N], nxt[N << 1], to[N << 1], E;
inline void add(int x, int y)
{
nxt[E] = head[x]; to[E] = y; head[x] = E ++;
}
vector<int> q; int F[N];
void dfs(int u, int fa)
{
F[u] = fa;
for(int i = head[u]; ~i; i = nxt[i])
{
int x = to[i];
if(x == fa) continue;
dfs(x, u);
}
q.push_back(u);
}
ll f[N][N], g[N][N], h[N][N];
int main()
{
ios :: sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T; cin >> T;
while(T -- )
{
cin >> n; E = 0; memset(head, -1, sizeof head); q.clear();
rep(i, 1, n) cin >> s[i];
rep(i, 1, n - 1)
{
int x, y; cin >> x >> y; add(x, y), add(y, x);
}
dfs(1, -1);
rep(i, 1, n) rep(j, 1, n) f[i][j] = g[i][j] = h[i][j] = 0;
for(auto u : q) for(auto v : q)
{
if(s[u] == s[v])
{
ll sm = 1;
for(int i = head[u]; ~i; i = nxt[i]) for(int j = head[v]; ~j; j = nxt[j])
{
if(to[i] == F[u] || to[j] == F[v]) continue;
int x = to[i], y = to[j]; sm += g[x][y]; if(sm>P) sm-=P;
}
f[u][v] = sm;
} h[u][v] = f[u][v];
for(int i = head[u]; ~i; i = nxt[i]) if(to[i] != F[u])
{
int x = to[i]; h[u][v] += h[x][v]; if(h[u][v] > P) h[u][v] -= P;
} g[u][v] = h[u][v];
for(int i = head[v]; ~i; i = nxt[i]) if(to[i] != F[v])
{
int y = to[i]; g[u][v] += g[u][y]; if(g[u][v]>P) g[u][v]-=P;
}
}
rep(i, 1, n) cout << g[i][i] << ' ';
cout << '\n';
}
return 0;
}