树上启发式合并+点分治思想 CF741D

Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

大意:

一棵根为1 的树,每条边上有一个字符(a-v共22种)。 一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经过重新排序后可以变成一个回文串。 求每个子树中最长的Dokhtar-kosh路径的长度。

思路:
迄今为止做掉的cf评分最高的一题(但是好像没那么吃力?),据说是dsu on tree 算法的发明者出的题。只能说确实很考验对该算法的理解

其实不难发现这道题里面有点分治的影子,因为它考虑的本身就是点对之间的问题。但是题目又限制了要在子树之内,而dsu on tree 又是处理静态子树问题的一大利器,所以不难往这两个方向来考虑(但是正常人怎么会把这两个叠在一起写啊)

因为这里字符串可以重排,所以我们只用考虑字母的奇偶性。再加上字母数不是很多,我们直接按位状压,某一位为1就代表改字母出现了奇数次(特意强调只有22个字母,摆明了就是要状压),那么,如果点对u,v之间的路径的异或和等于0或者是2的幂次,这条路径重排之后就是一个回文串了

如果看过我的另一篇博客的话,会发现这种树上点对异或的东西还是比较套路的。

我们考虑pre_xor数组来表示每一个点到1的前缀异或和,那么点u,对应的路径的异或和就是

pre_xor[u]^pre_xor[v]^pre_xor[lca]^pre_xor[lca]=pre_xor[u]^pre_xor[v]

这样就转化到了点对之间O(1)求路径异或和

再按照点分治的套路来分析一波

这里的点对路径无非就是三类:

1.过根u的路径且两个端点都不是u

这个就是我们不得不老老实实做的部分

3.路径不经过u,在u的子树之内

其实就是第一种情况,只不过过的根是u的子孙节点,我们只要在dfs的时候让父节点的答案更新一下就可以了

3.路径的一个端点是u

是第一种情况的特殊情况,处理手法跟第一个其实差不多,只有一点点细小差别

所以会发现真正要处理的其实只有第一种情况。

现在要求子树内最长的回文串。显然,如果u,v之间的路径满足条件的话,我们同样可以O(1)的得到路径长度=dep[u]+dep[v]-2*dep[lca]

所以我们加上dsu on tree的话,就是在第二遍dfs的时候同时记录一下子树前缀异或和为x的最大深度ex_xor,这样我们就可以直接在递归的时候不断更新答案了

具体怎么更新?

就是对于当前的pre_xor[u](不妨记为x),看看ex_xor[x],ex_xor[x^(1<

这样的话第一种情况就理完了,思路不算太难,但是挺少见

void check(ll id,ll fa)
{
    if(ex_xor[pre_xor[id]])
    {
        ma=max(ma,ex_xor[pre_xor[id]]+dep[id]-fa_dep);
    }
    for(int i=0;i<22;++i)
    {
        if(ex_xor[pre_xor[id]^(1ll<

check函数就是更新答案,add函数就是更新ex_xor数组,显然我们要先做check再做add操作

然后对于第三种情况,我们需要在子节点全部更新完毕之后再去查询(因为一头是当前子树的根节点),然后操作其实并没有太大差别,具体看代码吧

code

#include 
#include 
using namespace std;
#define endl '\n'
#define ll long long
const ll N=5e5+10;
struct ty
{
    ll t,l,next;
}edge[N<<1];
ll cn=0;
ll n,a,b;
char c;
ll head[N];
void add_edge(ll a,ll b,ll c)
{
    edge[++cn].t=b;
    edge[cn].l=c;
    edge[cn].next=head[a];
    head[a]=cn;
}
ll siz[N],son[N];
ll pre_xor[5000010];//树上前缀异或和
ll ex_xor[5000010];//前缀异或和为i的最深深度
ll dep[N];
ll ans[N];
ll Flag,fa_dep;
ll ma=0;
void init_dfs(ll id,ll fa)
{
    dep[id]=dep[fa]+1;
    siz[id]=1;
    ll sv=0;
    for(int i=head[id];i!=-1;i=edge[i].next)
    {
        ll y=edge[i].t;
        if(y==fa) continue;
        pre_xor[y]=pre_xor[id]^(1ll<sv)
        {
            sv=siz[y];
            son[id]=y;
        }
    }
}
void check(ll id,ll fa)
{
    if(ex_xor[pre_xor[id]])
    {
        ma=max(ma,ex_xor[pre_xor[id]]+dep[id]-fa_dep);
    }
    for(int i=0;i<22;++i)
    {
        if(ex_xor[pre_xor[id]^(1ll<>n;
    for(int i=2;i<=n;++i)
    {
        cin>>a>>c;
        ll id=c-'a';
        add_edge(a,i,id);
        add_edge(i,a,id);
    }
    init_dfs(1,0);
    dfs2(1,0,0);
    for(int i=1;i<=n;++i) cout<

你可能感兴趣的:(学习笔记,思维题,算法)