【树上静态链分治】【CF741D】Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths...

Description

给定一棵 \(n\) 个节点的树,每条边上有一个字符,字符集大小 \(22\),求每个节点的子树内最长的简单路径使得路径上的字符经过重排后构成回文串。

Limitation

\(1~\leq~n~\leq~500000,~\text{字符集}~=~['a',~'v']\)

Solution

考虑一条路径能构成回文串当且仅当路径上的字符出现奇数次的至多为一个。由于字符集大小很小,我们考虑状态压缩,即每个字符对应二进制位上的一个 \(1\)。考虑从每个点出发向下走,显然链长越深越好。于是我们对每个节点 \(u\) 的每个状态 \(S\) 维护从点 \(u\) 出发向下走路径状态为 \(S\) 的终点的最大深度。该点的答案易于求得,即为所有异或起来至多有一个 \(1\) 的状态组深度和减去两倍的该点深度。

考虑树上静态链分治(这是 lxl 的叫法,普遍叫法好像叫 DSU on tree),只用一个 \(f\) 数组维护全局信息

先进行轻重链剖分。考虑dfs的时候先递归轻儿子,递归结束后清空 \(f\) 数组,然后递归重儿子,递归结束后不清空 \(f\) 数组,枚举轻儿子的子树,将子树的信息加入 \(f\) 数组即可。考虑复杂度:每个节点的信息每次经过合并会增大一倍,于是复杂度 \(O(n~\log n)\)

事实上链分治的的流程为:

递归轻儿子,清空数据结构

递归重儿子,不清空数据结构

向数据结构中加入轻儿子及其子树的信息

Code

#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif

typedef long long int ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    int top=0;
    do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 500010;
const int upceil = 1 << 22;

int n, vistime;
int fa[maxn], ans[maxn], MU[maxn], son[maxn], sz[maxn], hash[maxn], frog[upceil], deepth[maxn], dfn[maxn], st[maxn], ed[maxn];
std::vector<int>sons[maxn];
std::vector<char>s[maxn];

void DFS(int);
void dfs(int, bool);

int main() {
    freopen("1.in", "r", stdin);
    qr(n);
    for (int i = 2; i <= n; ++i) {
        qr(fa[i]); int &ch = MU[i]; do ch = IPT::GetChar(); while ((ch > 'v') || (ch < 'a')); ch = 1 << (ch - 'a');
        sons[fa[i]].push_back(i);
    }
    deepth[1] = 1; DFS(1); dfs(1, true);
    for (int i = 1; i <= n; ++i) qw(ans[i], ' ', true);
    putchar('\n');
    return 0;
}

void DFS(int u) {
    sz[u] = 1; dfn[st[u] = ++vistime] = u;
    for (auto to : sons[u]) {
        hash[to] = hash[u] ^ MU[to]; deepth[to] = deepth[u] + 1; DFS(to);
        if (sz[to] > sz[son[u]]) son[u] = to;
        sz[u] += sz[to];
    } ed[u] = vistime;
}

void dfs(int u, bool rec) {
    if (!son[u]) {if (rec) frog[hash[u]] = std::max(frog[hash[u]], deepth[u]); return;}
    for (auto to : sons[u]) {
        if (to == son[u]) continue;
        dfs(to, false);
        ans[u] = std::max(ans[u], ans[to]);
    }
    dfs(son[u], true); ans[u] = std::max(ans[u], ans[son[u]]);
    ans[u] = std::max(ans[u], frog[hash[u]] - deepth[u]);
    for (int i = 1; i != upceil; i <<= 1) ans[u] = std::max(ans[u], frog[i ^ hash[u]] - deepth[u]);
    int dep = deepth[u] << 1;
    frog[hash[u]] = std::max(frog[hash[u]], deepth[u]);
    for (auto to : sons[u]) {
        if (to == son[u]) continue;
        for (int i = st[to]; i <= ed[to]; ++i) {
            int v = dfn[i], w = hash[v];
            if (frog[w]) ans[u] = std::max(ans[u], frog[w] + deepth[v] - dep);
            for (int j = 1; j != upceil; j <<= 1) {
                if (frog[w ^ j]) ans[u] = std::max(ans[u], frog[w ^ j] + deepth[v] - dep);
            }
        }
        for (int i = st[to]; i <= ed[to]; ++i) {
            frog[hash[dfn[i]]] = std::max(frog[hash[dfn[i]]], deepth[dfn[i]]);
        }
    }
    if (!rec) for (int i = st[u]; i <= ed[u]; ++i) frog[hash[dfn[i]]] = 0;
}

转载于:https://www.cnblogs.com/yifusuyi/p/10459649.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值