话说这道题能不能用线段树合并啊,没有细想,是学习树上启发式合并时做的。
题目
题意:
一棵根为
1
1
1 的树,每条边上有一个字符(
a
−
v
a-v
a−v 共
22
22
22 种)。 一条简单路径被称为
z
x
y
zxy
zxy 当且仅当路径上的字符经过重新排序后可以变成一个回文串。 求每个子树中最长的
z
x
y
zxy
zxy 路径的长度。
首先是个树上启发式合并。那么如何判回文串呢,我们只需要将字母奇偶性状态压缩进行树dp即可。还是比较板的,只是要注意一些细节。
vector<LL> G[MAXN];
LL n, dp[1 << 22], Val[MAXN], Dep[MAXN], Size[MAXN], Heavy[MAXN], Ans[MAXN];
// dp: Getting certain situation's max length
void Prepare(LL x)
{
Size[x] = 1;
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
Val[to] = Val[x] ^ (1 << Val[to]); // Val[x] -> Root to x's Ji/Ou
Dep[to] = Dep[x] + 1;
Prepare( to );
Size[x] += Size[to];
if (! Heavy[x] || Size[Heavy[x]] < Size[to])
Heavy[x] = to;
}
}
LL Maxx, More;
void GetUp(LL x)
{
dp[Val[x]] = Max(Dep[x], dp[Val[x]]);
}
void Update(LL x)
{
GetUp( x );
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
Update( to );
}
}
inline void Get(LL x)
{
if ( dp[Val[x]] )
Maxx = Max(Maxx, Dep[x] + dp[Val[x]] - More);
for (Int i = 0; i < 22; ++ i)
if (dp[(1 << i) ^ Val[x]])
Maxx = Max(Maxx, Dep[x] + dp[(1 << i) ^ Val[x]] - More);
}
void Calc(LL x)
{
Get( x );
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
Calc( G[x][i] );
}
void Clear(LL x)
{
dp[Val[x]] = 0;
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
Clear( G[x][i] );
}
void Dfs(LL x,bool Zez)
{
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
if (to != Heavy[x])
Dfs(to, 0);
} // Go light sons
if ( Heavy[x] )
Dfs(Heavy[x], 1); // Go heavy son
More = Dep[x] << 1;
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
Maxx = Max(Maxx, Ans[to]); // Merge son's answer
}
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
if (to != Heavy[x])
{
Calc( to );
Update( to );
}
} // Get son-tree's answer
Get( x ); GetUp( x ); // Update x's answer
Ans[x] = Maxx;
if (! Zez)
{
Clear( x );
Maxx = 0;
}
}
int main()
{
read( n );
for (Int i = 2; i <= n; ++ i)
{
LL u;
read( u );
G[u].push_back( i );
char c;
scanf("%c", &c);
Val[i] = c - 'a';
}
Dep[1] = 1;
Prepare( 1 );
Dfs(1, 1);
for (Int i = 1; i <= n; ++ i)
printf("%lld ", Ans[i]);
}