D. Hossam and (sub-)palindromic tree—树形dp
cf1771 Hossam and (sub-)palindromic tree
https://codeforces.ml/contest/1771/problem/D
题意
给出一颗n个节点的树,树上的每个节点都代表一个小写英文字母,求所有的\(s(u, v)\)(u到v的一条简单路径)中是回文串的最长子序列的长度。
思路
如果我们不考虑在树上,普通求最长回文子序列的长度是:
对于区间[u, v]
如果u == v dp[u][v] = 1
如果v == u + 1
(1)s[u] == s[v] dp[u][v] = 2
(2)s[u] != s[v] dp[u][v] = 1
其他
dp[u][v] = max(dp[u + 1][v], dp[u][v + 1])
s[u] == s[v] dp[u][v] = max(dp[u][v], d[u + 1][v - 1] + 2)
那对于树上的,如果我们能得出u, v的下内的节点是什么,那我们也可以类似做了。
因为节点数不多,我们可以以每个节点为根进行dfs,求出每个各节点相应情况下的nxt值
然后递归dp即可
#include <bits/stdc++.h>
#include <stdlib.h>
using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define ll long long
#define int ll
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e3 + 5;
const int M = 1e6 + 5;
const ll mod = 1e9 + 7;
int n;
string s;
vector<int>g[N];
int nxt[N][N];
int dp[N][N];
void dfs(int root, int x, int fa){
for(auto to : g[x]){
if(to == fa) continue;
nxt[root][to] = x;
dfs(root, to, x);
}
}
inline void cal(int u, int v){
if(dp[u][v] != -1) return;
if(u == v){
dp[u][v] = 1;
return;
}
if(nxt[u][v] == u){
if(s[u] == s[v]) dp[u][v] = 2;
else dp[u][v] = 1;
return;
}
if(dp[u][nxt[u][v]] == -1) cal(u, nxt[u][v]);
if(dp[nxt[v][u]][v] == -1) cal(nxt[v][u], v);
dp[u][v] = max(dp[u][nxt[u][v]], dp[nxt[v][u]][v]);
if(s[u] == s[v]){
cal(nxt[v][u], nxt[u][v]);
dp[u][v] = max(dp[u][v], dp[nxt[v][u]][nxt[u][v]] + 2);
}
}
void init(){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
dp[i][j] = -1;
nxt[i][j] = 0;
}
}
for(int i = 1; i <= n; i++) g[i].clear();
}
void solve()
{
cin >> n;
cin >> s;
s = " " + s;
init();
for(int i = 1, u, v; i < n; i++){
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i = 1; i <= n; i++)
dfs(i, i, -1);
int ans = 1;
for(int i = 1; i <= n; i++){
for(int j = i; j <= n; j++){
cal(i, j);
cal(j, i);
ans = max({ans, dp[i][j], dp[j][i]});
//cerr << ans << "\n";
}
}
cout << ans << "\n";
}
signed main()
{
IOS;
int t = 1;
cin >> t;
while (t--)
{
solve();
}
}