zoukankan      html  css  js  c++  java
  • 【洛谷6623】[省选联考 2020 A 卷] 树(有趣的Trie树题)

    点此看题面

    大致题意: 给定一棵树,节点(i)点权为(v_i)。定义(val(x)=xor_y(v_y+d(x,y))),其中(y)(x)子树内的点(包括(x)自身),(d(x,y))(x,y)的树上距离,求(sum_{i=1}^nval(i))

    前言

    (Trie)树的这种套路之前接触过,因此很快就做了出来。

    由于这是一道挺有趣的题,于是就写了写。

    题意转化

    由于(val(x))的求解对象是子树内的点,容易想到先求出子树内的答案,然后通过数据结构合并得到父节点的答案。

    考虑子节点的答案到了父节点,所有距离加(1),同时还要统计上父节点自身的贡献。

    也就是要支持下列几项操作:增加一个数、合并、求总异或和、给所有数加(1)

    看到异或想到(Trie)树,且前三项操作显然都很容易就可以用(Trie)树维护。

    而最后的给所有数加(1),其实有一个套路做法。

    给所有数加(1)

    与一般(Trie)树相反,考虑我们从低位到高位建一棵(Trie)树。

    每次加(1),原先的(0)变成了(1),原先的(1)变成了(0),然后还要进位加(1)

    其实也就是,交换左右儿子,然后递归继续给新的左儿子加(1)

    于是这道题就做完了,具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 525010
    using namespace std;
    int n,a[N+5],f[N+5],Rt[N+5],ans[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    }F;
    class Trie
    {
    	private:
    		#define LN 25
    		int Nt;struct node {int V,S[2];}O[N*LN<<1];
    	public:
    		I void Ins(int& rt,CI x,CI d=0)//增加一个数
    		{
    			if(d==LN) return;!rt&&(rt=++Nt),O[rt].V^=1,Ins(O[rt].S[x&1],x>>1,d+1);
    		}
    		I void U(CI rt,int& res,CI d=0)//给所有数加1
    		{
    			if(!rt) return;res^=(O[O[rt].S[0]].V^O[O[rt].S[1]].V)<<d,//更新这一位的答案
    			swap(O[rt].S[0],O[rt].S[1]),U(O[rt].S[0],res,d+1);//交换左右儿子,接着递归处理进位
    		}
    		I void Merge(int& x,CI y)//Trie树合并
    		{
    			if(!x||!y) return (void)(x|=y);O[x].V^=O[y].V,
    			Merge(O[x].S[0],O[y].S[0]),Merge(O[x].S[1],O[y].S[1]);
    		}
    }T;
    int main()
    {
    	RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i]);for(i=2;i<=n;++i) F.read(f[i]);
    	long long t=0;for(i=n;i;--i) T.U(Rt[i],ans[i]),T.Ins(Rt[i],a[i]),//子树内所有点距离加1,然后统计当前点贡献
    		ans[i]^=a[i],t+=ans[i],i^1&&(T.Merge(Rt[f[i]],Rt[i]),ans[f[i]]^=ans[i]);//计算答案,然后把Trie树合并给父节点
    	return printf("%lld
    ",t),0;
    }
    
  • 相关阅读:
    在shell脚本中执行shell脚本
    通过shell脚本批处理es数据
    JOIN a table with a subquery
    学习率 测试
    步长为float
    解决pycharm问题:module 'pip' has no attribute 'main'
    段错误 “段错误(segment fault)”、“非法操作,该内存地址不能read/write” 非法指针解引用造成的错误。
    快速删除指定目录的指定条件的文件
    包依赖管理 项目的分布式迁移
    判断操作系统
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6623.html
Copyright © 2011-2022 走看看