741D - Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并

12 篇文章 0 订阅

题意:

给你一棵树,每条边有个字符,问对于每个节点,他的子树中最长的好路径是多长。

好定义为把路径中字符排列后可以变成回文。

题解:

路径可以成回文条件是出现次数为奇数的字符数量 <= 1。

对于每个点我们用二进制记录他到根节点路径的异或和d[i](把每个字符看成一位)。

那么任意路径出现的字符次数就是 d[i] ^ d[j] 。

现在要求每个子树,启发式合并即可。

还可以用预处理dfs序,这样每次edt就不用递归了,直接for循环,优化常数和可读性。

代码:

#include <bits/stdc++.h>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int MOD = 1e9 + 7;
const double PI = acos (-1.);
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 5e5 + 5;

vector<pii> G[MAXN];
int d[MAXN];
int cnt[1<<22];
int siz[MAXN], son[MAXN], dep[MAXN];

void pre_dfs (int now, int par, int dd = 1) {
    siz[now] = 1;
    dep[now] = dd;
    for (pii i : G[now]) {
        d[i.fir] = d[now] ^ (1 << i.sec);
        pre_dfs (i.fir, now, dd + 1);
        if (siz[i.fir] > siz[son[now]]) son[now] = i.fir;
        siz[now] += siz[i.fir];
    }
}

int res[MAXN];

void edt (int now, int top) {
    int v = d[now];
    for (int i = 0; i < 22; i++) {
        int o = v ^ (1<<i);
        if (cnt[o])  res[top] = max(res[top], dep[now] - dep[top] * 2 + cnt[o]);
    }
    if (cnt[v]) res[top] = max(res[top], dep[now] - dep[top] * 2 + cnt[v]);
    for (auto i : G[now]) {
        edt(i.fir, top);
    }
}

void add (int now) {
    cnt[d[now]] = max(cnt[d[now]], dep[now]);
    for (pii i : G[now]) add (i.fir);
}

void clr (int now) {
    cnt[d[now]] = 0;
    for (pii i : G[now]) {
        clr (i.fir);
    }
}

void dfs (int now, int par, int kep) {
    for (auto i : G[now]) {
        if (i.fir == par || i.fir == son[now]) continue;
        dfs (i.fir, now, 0);
        res[now] = max(res[now], res[i.fir]);
    }
    if (son[now]) dfs(son[now], now, 1), res[now] = max(res[now], res[son[now]]);;
    if (cnt[d[now]]) res[now] = max(res[now], cnt[d[now]] - dep[now]);
    for (int i = 0; i < 22; i++) {
        int o = d[now] ^ (1 << i);
        if (cnt[o])
            res[now] = max(res[now], -dep[now] + cnt[o]);
    }
   // if (now == 2) debug(son[now])
    cnt[d[now]] = max(cnt[d[now]], dep[now]);
    for (auto i : G[now])
        if (i.fir != son[now]) {
            edt(i.fir, now);
            add(i.fir);
        }

    if (!kep) clr (now);

}

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    int n;
    scanf ("%d", &n);
    for (int i = 1; i < n; i++) {
        char c[2];
        int p;
        scanf ("%d %s", &p, c);
        G[p].pb (mp(i + 1, c[0] - 'a'));
    }
    pre_dfs(1, 0);
    dfs(1, 0, 0);
    for (int i = 1; i <= n; i++) printf ("%d ", res[i]);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值