Problem Link: CF 741D
Soution:
注意到字母最大是v,即最多有22种字符,看到这个数据范围就想到利用状压。那么在这题怎么用上状压呢?想到一个性质:一个简单路径可以通过打乱变成回文串,当且仅当在这个路径上出现的次数为奇数的字符个数不超过一个。
我们状压记录每个字符出现的奇偶性,偶数为0,奇数为1,那么对于以
x
x
x为根的子树中,记
s
t
a
p
sta_p
stap为p点到x的路径状态,
s
t
a
q
sta_q
staq为q点到x的路径状态,那么显然这个总路径的状态就是
s
t
a
p
sta_p
stap ^
s
t
a
q
sta_q
staq,暂记为
r
e
t
ret
ret,显然路径合法的条件是:
r
e
t
ret
ret中1的个数
≤
1
\leq 1
≤1。
知道了如何记录,先考虑暴力求解。
s
u
b
t
a
s
k
1
:
subtask_1:
subtask1:从根节点x开始搜索,记录所有路径的状态,如何找到这个状态是否和其他链异或起来有合法解?其实也简单,考虑到异或性质的特殊性,即x ^ y = z,则x ^ z = y.那么把当前的路径状态和所有的合法状态(显然只有23种)异或一下,结果暂记为
n
o
w
now
now,再查找是否有
n
o
w
now
now这个状态就行了。
s
u
b
t
a
s
k
2
:
subtask_2:
subtask2:如何得出答案?先求出所有节点的深度,记为
d
e
p
i
dep_i
depi,那么对于上述的合法状态,答案就是
d
e
p
p
+
d
e
p
q
−
2
∗
d
e
p
x
dep_p + dep_q - 2 * dep_x
depp+depq−2∗depx,可以看出
d
e
p
p
dep_p
depp和
d
e
p
q
dep_q
depq越大越好,所以对于有相同状态的路径,只需要记录深度最深的那个就行了,这样复杂度也有保证。
上述的暴力求解因为每次要清空状态数组重新记录,复杂度
O
(
23
∗
n
2
)
O(23 * n^2)
O(23∗n2),无法通过,想到树的特殊性,题目又是静态的没有修改,加入树上启发式合并,复杂度降为
O
(
23
∗
n
∗
l
o
g
2
n
)
O(23 * n * log_2n)
O(23∗n∗log2n),就可以通过啦。
代码得思考一下怎么写。
AC Code:
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define endl "\n"
#define fi first
#define se second
#define db double
#define gcd __gcd
#define pb push_back
#define mp make_pair
#define lowbit(x) (x & (-x))
#define PII pair<int, int>
#define all(x) x.begin(), x.end()
#define debug(x) cout << #x << " = " << x << endl
#define rep(i, a, b) for(__typeof(b) i = a; i <= (b); i++)
#define Rep(i, a, b) for(__typeof(a) i = a; i >= (b); i--)
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
template<class T> inline T qmin(T a, T b) { return a < b ? a : b; }
template<class T> inline T qmax(T a, T b) { return a > b ? a : b; }
typedef long long ll;
typedef unsigned long long ull;
const db eps = 1e-9;
const db PI = acos(-1);
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)5e5 + 5;//remember to modify it, No RE or MLE
const ll INF = 0x3f3f3f3f3f3f3f3f;
using namespace std;
int n;
vector<PII> G[maxn];
int sz[maxn], Wson[maxn], dep[maxn];
int Son;
int ans[maxn], cnt[26];
int sta[1<<22], xor_sum[maxn];
void Max(int &a, int b){
a = max(a, b);
}
int lg[30];
void init(){
rep(i, 0, 25) lg[i] = 1 << i;
}
void dfs1(int u){
sz[u] = 1;
for(PII to : G[u]){
int v = to.fi;
xor_sum[v] = xor_sum[u] ^ to.se;
dep[v] = dep[u] + 1;
dfs1(v);
sz[u] += sz[v];
if(sz[v] > sz[Wson[u]]) Wson[u] = v;
}
}
void work1(int u, int root){
if(sta[xor_sum[u]]) Max(ans[root], sta[xor_sum[u]] + dep[u] - 2 * dep[root]);
rep(i, 0, 21) if(sta[xor_sum[u]^lg[i]]) Max(ans[root], sta[xor_sum[u]^lg[i]] + dep[u] - 2 * dep[root]);
for(PII to : G[u]){
int v = to.fi;
work1(v, root);
}
}
void work2(int u, int opt){
if(opt == 1) Max(sta[xor_sum[u]], dep[u]);
else sta[xor_sum[u]] = 0;
for(PII to : G[u]){
int v = to.fi;
work2(v, opt);
}
}
void dfs2(int u, int opt){
for(PII to : G[u]){
int v = to.fi;
if(v != Wson[u]) dfs2(v, 1), Max(ans[u], ans[v]);
}
if(Wson[u]) dfs2(Wson[u], 0), Max(ans[u], ans[Wson[u]]);
Max(sta[xor_sum[u]], dep[u]);
Max(ans[u], sta[xor_sum[u]] - dep[u]);
rep(i, 0, 21) if(sta[xor_sum[u]^lg[i]]) Max(ans[u], sta[xor_sum[u]^lg[i]] - dep[u]);
for(PII to : G[u]){
int v = to.fi;
if(v == Wson[u]) continue;
work1(v, u);
work2(v, 1);
}
if(opt) work2(u, 0);
}
int main()
{
init();
scanf("%d", &n);
rep(i, 2, n){
int x; char c; scanf("%d %c", &x, &c);
G[x].pb(mp(i, lg[c-'a']));
}
dep[1] = 1;
dfs1(1);
dfs2(1, 0);
rep(i, 1, n) printf("%d%c", ans[i], i == n ? '\n' : ' ');
return 0;
}