Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths【Codeforces 741 D】【Dsu on Tree + 状压】

Codeforces Round #383 (Div. 1) D


  着实难做!

  首先,题意,题意不是一棵子树上任意构成!!!

  题意

  问的是对应子树v,它上面的简单路径(链),所能构成的最长的回文长度。然后输出对应的1~N的所有以它为子树根结点的最长回文长度(不强制要求经过根结点)。

  思路

  首先,我们考虑因为是可以打乱顺序的,所以,如果构成回文,要么有唯一一个符号是出现奇数次的,要么就是没有一个符号出现奇数次,这两个都是可以构成回文的,又因为这里有奇偶,开始往0、1方向上想,若为奇数则为1,若为偶数则为0,岂不就是一个xor的思想了嘛,这里存档。

  再者,xor有性质,如果a ^ b == 0则说明a和b加起来一定是个偶数了,如果说a ^ b得到的值在二进制中只有唯一一次出现1,那么它也是满足条件的。这样,情况一的时候我们可以直接知道,情况二考虑奇数唯一时候,我们可以1~22的枚举,复杂度O(22)就是可以的了。

  然后,就是维护了,因为这里的信息涉及到了子树的关系上去,可以往Dsu上面想想。我们需要确定子树信息,我们不难知道以某个点作为子树的根节点的时候,它所有子树下的链与他构成回文串的情况。1~22枚举哪一位是奇数,然后现在从原树根节点到目前点的xor值是Xor[u],于是有Xor[u] ^ (1 << i)如果是存在的,我们就可以去更新答案,进行比较了。

  然后的话,就是更新这个子树了,这里用Dsu进行优化,将复杂度变为O(N * log(N))。我们对重儿子的答案不做删除,对轻儿子的答案直接更新,更新过程中,我们依然要与答案进行比较,为什么呢?可能将子树的根结点作为链中间的节点进行答案统计,这里的话直接deep[u] + deep[v] - deep[lca] * 2的做法就可以了,更新完成之后删除。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#define _ABS(x, y) ( x > y ? (x - y) : (y - x) )
#define lowbit(x) ( x&( -x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define efs 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 5e5 + 7;
inline int _real(int x) { return (1 << x); }
int N, head[maxN], cnt, Xor[1 << 22] = {0};
struct Eddge
{
    int nex, to, val;
    Eddge(int a=-1, int b=0, int c=0):nex(a), to(b), val(c) {}
}edge[maxN];
inline void addEddge(int u, int v, int val)
{
    edge[cnt] = Eddge(head[u], v, val);
    head[u] = cnt++;
}
int Wson[maxN], siz[maxN], Wedge[maxN], id[maxN], rid[maxN], tot = 0, its_end[maxN], deep[maxN];
void pre_dfs(int u)
{
    id[u] = ++tot;
    rid[tot] = u;
    siz[u] = 1;
    int maxx = 0;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        Xor[v] = Xor[u] ^ (1 << edge[i].val);   //求出每一条简单路径的贡献
        deep[v] = deep[u] + 1;
        pre_dfs(v);
        siz[u] += siz[v];
        if(siz[v] > maxx)
        {
            Wson[u] = v;
            Wedge[u] = edge[i].val;
            maxx = siz[v];
        }
    }
    its_end[u] = tot;
}
int ans[maxN] = {0}, F[1 << 22] = {0};
bool letter[27] = {false};
void dfs(int u, int op)
{
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == Wson[u]) continue;
        dfs(v, 0);
        ans[u] = max(ans[u], ans[v]);
    }
    if(Wson[u])
    {
        dfs(Wson[u], 1);
        ans[u] = max(ans[u], ans[Wson[u]]);
    }
    if(F[Xor[u]]) ans[u] = max(ans[u], F[Xor[u]] - deep[u]);    //完全相等
    for(int i=0; i<22; i++) //最多有一个差别的情况
    {
        if(F[Xor[u] ^ (1 << i)]) ans[u] = max(ans[u], F[Xor[u] ^ (1 << i)] - deep[u]);
    }
    F[Xor[u]] = max(F[Xor[u]], deep[u]);
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == Wson[u]) continue;
        for(int j=id[v], w; j<=its_end[v]; j++)
        {
            w = rid[j];
            if(F[Xor[w]]) ans[u] = max(ans[u], F[Xor[w]] + deep[w] - (deep[u] << 1));
            for(int k=0; k<22; k++)
            {
                if(F[Xor[w] ^ (1 << k)]) ans[u] = max(ans[u], F[Xor[w] ^ (1 << k)] + deep[w] - (deep[u] << 1));
            }
        }
        for(int j=id[v], w; j<=its_end[v]; j++)
        {
            w = rid[j];
            F[Xor[w]] = max(F[Xor[w]], deep[w]);
        }
    }
    if(!op)
    {
        for(int i=id[u]; i<=its_end[u]; i++) F[Xor[rid[i]]] = 0;
    }
}
inline void init()
{
    cnt = 0;
    for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
    scanf("%d", &N);
    init();
    int pf;
    char s[3];
    for(int i=2; i<=N; i++)
    {
        scanf("%d%s", &pf, s);
        addEddge(pf, i, s[0] - 'a');
    }
    pre_dfs(1);
    dfs(1, 0);
    for(int i=1; i<=N; i++) printf("%d%c", ans[i], i == N ? '\n' : ' ');
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值