zoukankan      html  css  js  c++  java
  • CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths——dsu on tree

    题目描述

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

    思路

      不多的只能用树上启发式合并做的题。。。虽然算法很暴力,但是本题还是挺难想的。

      考虑一共只有22种不同的边权,我们要找的是经过重新排序后回文的路径,也就是说:为偶数时,必须有两两相同的;为奇数时,最多只能有一个多出来的。那么我们考虑状压,对第$i$种边权压成$1<<i-1$,这样我们找的路径就变成了:路径上所有边的异或和为0或者为22中状态的一种。(为什么要状压呢?随便举几个栗子就知道了)

      我们先处理出$d[i]表示i到根的异或和,那么任意路径(u,v)的异或和就是d[u] oplus d[v],暴力统计时,我们先遍历一棵轻儿子,遍历完后再把轻儿子的贡献加入桶中,这样就可以做到让u变成此次暴力的lca,于是我们在now结点的子树中遍历到(u,v)时,先统计桶中有没有d[u],这样异或起来为0,再统计有没有和d[u]异或起来为2^i$的即可。

    code

    #include<bits/stdc++.h>
    #define I inline
    using namespace std;
    const int N=1000010;
    const int inf=(1<<31)-1;
    int val[N],n;
    struct node
    {
        int to,nxt,w;
    }g[N];
    int head[N],cnt;
    
    int d[N],sz[N],son[N],Son,buk[1<<22],now,dep[N],ans[N];
    
    I int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    
    I void addedge(int u,int v,int w)
    {
        g[++cnt].nxt=head[u];
        g[cnt].to=v;
        g[cnt].w=w;
        head[u]=cnt;
    }
    
    I void get_son(int u)
    {
        sz[u]=1;
        for(int i=head[u];i;i=g[i].nxt)
        {
            int v=g[i].to,w=g[i].w;
            d[v]=d[u]^w;dep[v]=dep[u]+1;
            get_son(v);
            sz[u]+=sz[v];
            if(sz[v]>sz[son[u]])son[u]=v;
        }
    }
    
    I void init(int u)
    {
        buk[d[u]]=-inf;
        for(int i=head[u];i;i=g[i].nxt)init(g[i].to);
    }
    
    I void get(int u)
    {
        ans[now]=max(ans[now],dep[u]+buk[d[u]]);
        for(int i=0;i<=21;i++)ans[now]=max(ans[now],dep[u]+buk[(1<<i)^d[u]]);
        for(int i=head[u];i;i=g[i].nxt)
        {
            int v=g[i].to;get(v);
        }
    }
    
    I void add(int u)
    {
        buk[d[u]]=max(buk[d[u]],dep[u]);
        for(int i=head[u];i;i=g[i].nxt)
        {
            int v=g[i].to;add(v);
        }
    }
            
    
    I void dfs(int u,bool op)
    {
        for(int i=head[u];i;i=g[i].nxt)
        {
            int v=g[i].to;
            if(v==son[u])continue;
            dfs(v,0);
        }
        if(son[u])dfs(son[u],1),Son=son[u];
        now=u;
        for(int i=head[u];i;i=g[i].nxt)
        {
            int v=g[i].to;
            if(v==Son)continue;
            get(v);add(v);
        }
        buk[d[u]]=max(buk[d[u]],dep[u]);
        ans[u]=max(ans[u],buk[d[u]]+dep[u]);
        for(int i=0;i<=21;i++)ans[u]=max(ans[u],dep[u]+buk[(1<<i)^d[u]]);
        ans[u]-=dep[u]<<1;
        for(int i=head[u];i;i=g[i].nxt)
        {
            int v=g[i].to;
            ans[u]=max(ans[u],ans[v]);
        }
        if(!op)init(u);
    }
        
    int main()
    {
        n=read();
        memset(buk,128,sizeof(buk));
        for(int i=2;i<=n;i++)
        {
            int x=read();
            char ch=getchar();
            while(ch<'a'||ch>'v')ch=getchar();
            addedge(x,i,(1<<(ch-'a')));
            d[i]=1<<(ch-'a');
        }
        get_son(1);
        dfs(1,0);
        for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    }
  • 相关阅读:
    day04作业
    一个简单的gridlayout栗子
    用户名、密码等15个常用的js正则表达式
    html 颜色
    心态好的人,一辈子都好
    怎么样好好的聊天呢
    一篇引用文章
    再见,发微信不回的人
    第一个不怎么正经的网页
    关于学科目标
  • 原文地址:https://www.cnblogs.com/THRANDUil/p/11645234.html
Copyright © 2011-2022 走看看