loj3303.「联合省选 2020 A」树

题目链接

首先有自然的思路就是 dfs 一遍整棵树,然后把儿子的信息合并起来加上自己的求一个答案。

求异或和,想到 01-trie。这一类数据结构合并都很没有营养,模仿线段树合并的过程直接合并就行,麻烦在于全局 +1 的操作。

考虑一种特殊的 01-trie。我们不是按照求异或最大值问题的模型,从高位到低位建树,因为这样进位无法处理,我们从低位到高位建树,这样进位只要递归就能继续处理。

考虑 +1 时,如果走到一个节点,那么 \(0+1\) 变成 \(1\)\(1+1\) 进位之后变成了 \(0\),相当于交换了左右儿子,然后还需要递归原来是 \(1\) 的那一边处理进位。但是我们需要自下而上更新异或和,维护方法是左右儿子异或和异或起来,再异或上右儿子那边的数字个数的奇偶性(即 \(1\) 的个数的奇偶性)。所以需要先递归 \(1\) 的子树再交换儿子以保持答案正确性。

这样就很简单了,dfs 一遍树,到每个点先合并所有儿子,然后全局 +1,最后插入自己的权值,然后算贡献即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define int long long
struct edge
{
    int nxt,to;
}e[600001];
int n,tot,h[600001],v[600001],cnt,ch[600001*71][2],val[600001*71],ans;
bool s[600001*71];
inline int read()
{
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
}
inline void add(int x,int y)
{
    e[++tot].nxt=h[x];
    h[x]=tot;
    e[tot].to=y;
}
inline void push_up(int k,int d)
{
    val[k]=val[ch[k][0]]^val[ch[k][1]]^(s[ch[k][1]]<<d);
}
inline int merge(int x,int y,int d)
{
    if(ch[y][0])
    {
        if(ch[x][0])
            ch[x][0]=merge(ch[x][0],ch[y][0],d+1);
        else
            ch[x][0]=ch[y][0];
    }
    if(ch[y][1])
    {
        if(ch[x][1])
            ch[x][1]=merge(ch[x][1],ch[y][1],d+1);
        else
            ch[x][1]=ch[y][1];
    }
    s[x]^=s[y];
    push_up(x,d);
    return x;
}
void insert(int x,int node,int d)
{
    if(d>23)
        return;
    s[node]^=1;
    if(!ch[node][x&1])
        ch[node][x&1]=++cnt;
    insert(x>>1,ch[node][x&1],d+1);
    push_up(node,d);
}
void update(int node,int d)
{
    if(!node||d>23)
        return;
    update(ch[node][1],d+1);
    ch[node][0]^=ch[node][1]^=ch[node][0]^=ch[node][1];
    push_up(node,d);
}
void dfs(int k)
{
    for(register int i=h[k];i;i=e[i].nxt)
    {
        dfs(e[i].to);
        merge(k,e[i].to,0);
    }
    update(k,0);
    insert(v[k],k,0);
    ans+=val[k];
}
signed main()
{
    cnt=n=read();
    for(register int i=1;i<=n;++i)
        v[i]=read();
    for(register int i=2;i<=n;++i)
    {
        int f=read();
        add(f,i);
    }
    dfs(1);
    printf("%lld\n",ans);
    return 0;
}
posted @ 2021-08-24 14:45  绝顶我为峰  阅读(26)  评论(0编辑  收藏  举报