zoukankan      html  css  js  c++  java
  • 【洛谷P6623】树

    题目

    题目链接:https://www.luogu.com.cn/problem/P6623
    给定一棵 \(n\) 个结点的有根树 \(T\),结点从 \(1\) 开始编号,根结点为 \(1\) 号结点,每个结点有一个正整数权值 \(v_i\)
    \(x\) 号结点的子树内(包含 \(x\) 自身)的所有结点编号为 \(c_1,c_2,\dots,c_k\),定义 \(x\) 的价值为:

    \[val(x)=(v_{c_1}+d(c_1,x)) \oplus (v_{c_2}+d(c_2,x)) \oplus \cdots \oplus (v_{c_k}+d(c_k, x)) \]

    其中 \(d(x,y)\) 表示树上 \(x\) 号结点与 \(y\) 号结点间唯一简单路径所包含的边数,\(d(x,x) = 0\)\(\oplus\) 表示异或运算。
    请你求出 \(\sum\limits_{i=1}^n val(i)\) 的结果。

    思路

    考虑从答案从 \(x\) 的子节点如何转移到 \(x\) 上来:显然是每一个节点的权值加一后再异或起来。
    把每一个节点的权值 + 到目前根的距离转成二进制,由低位到高位扔进一棵 Trie 中,那么把所有子树内的点权值加一,其实就是沿着 Trie 边权为 1 的点走下去,并且把沿路遇到的 0 给变成 1,再把沿路的 1 变为 0。那么其实就是将遍历到的节点的左右子树交换了。
    插入自己本身的权值简单,那么我们就成功搞定加一操作和插入操作。我们只需要将 \(x\) 的各个子节点的 Trie 合并到 \(x\) 上。类似于线段树合并。
    时间复杂度 \(O(n\log n)\)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N=525020,LG=25;
    int n,tot,a[N],head[N],rt[N];
    ll ans;
    
    struct edge
    {
    	int next,to;
    }e[N];
    
    void add(int from,int to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    struct Trie
    {
    	int tot,lc[N*LG],rc[N*LG],val[N*LG],size[N*LG];
    	
    	void pushup(int x,int dep)
    	{
    		val[x]=val[lc[x]]^val[rc[x]];
    		if (size[rc[x]]&1) val[x]^=(1<<dep);
    	}
    	
    	int merge(int x,int y,int dep)
    	{
    		if (!x || !y) return x+y;
    		size[x]+=size[y];
    		lc[x]=merge(lc[x],lc[y],dep+1);
    		rc[x]=merge(rc[x],rc[y],dep+1);
    		pushup(x,dep);
    		return x;
    	}
    	
    	void update(int x,int dep)
    	{
    		if (!x) return;
    		swap(lc[x],rc[x]);
    		update(lc[x],dep+1);
    		pushup(x,dep);
    	}
    	
    	int ins(int x,int val,int dep)
    	{
    		if (dep>22) return 0;
    		if (!x) x=++tot;
    		size[x]++;
    		if (val&(1<<dep)) rc[x]=ins(rc[x],val,dep+1);
    			else lc[x]=ins(lc[x],val,dep+1);
    		pushup(x,dep);
    		return x;
    	}
    }trie;
    
    void dfs(int x)
    {
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		dfs(e[i].to);
    		rt[x]=trie.merge(rt[x],rt[e[i].to],0);
    	}
    	trie.update(rt[x],0);
    	rt[x]=trie.ins(rt[x],a[x],0);
    	ans+=trie.val[rt[x]];
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	for (int i=2,x;i<=n;i++)
    	{
    		scanf("%d",&x);
    		add(x,i);
    	}
    	dfs(1);
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    vue this,$set方法
    表格的拖拽排序功能---应用splice方法
    ES6方法的特性总结
    template functional
    scrollTop, offsetTop, pageYOffset, scrollY 的区别
    Sass @mixin 与 @include
    关于Vue中props的详解
    前端开发工具宝典
    前端js开发常用的60种工具方法
    element ui table表格里面插槽的使用方法
  • 原文地址:https://www.cnblogs.com/stoorz/p/13199533.html
Copyright © 2011-2022 走看看