6714. 【2020.06.10省选模拟】题2 树

题目


正解

之前做ATCoder见过这样的题,可是没有看懂题解。
(不过这也似乎不是题解做法)

如果只有加法或者异或,那么这题显然是个水题。
随便找一个点作为根。修改某个点的时候,暴力修改它的父亲,在自己身上打标记。询问某个点的时候,结合自身的信息和父亲身上的标记。

用专业的话来说,在每个点上维护一个权值,然后在父亲上维护一个置换。用这个权值进行置换,就得到了真正的权值。
现在考虑去如何维护这个置换。
假如将所有的数拿出来从低位到高位建个 T r i e Trie Trie,考虑操作之后会变得怎么样。异或显然,加一相当于是一段连续的前缀 1 1 1变成 0 0 0,再后面一位变成 1 1 1(也就是 1110... 1110... 1110...变成 0001... 0001... 0001...这样)。把这个操作对应到 T r i e Trie Trie上的子树,可以发现这其实就是这样的过程:
从根节点开始,交换左右儿子,然后进入新的左儿子,继续交换左右儿子,再进入新的左儿子……如此操作。
这样可以发现一次加一操作的时间复杂度是 O ( lg ⁡ 1 e 9 ) O(\lg 1e9) O(lg1e9)的。至于异或,直接打标记就可以维护。

通过这个 T r i e Trie Trie,可以维护置换和逆置换。
修改某个点上的值的时候,先通过父亲上的 T r i e Trie Trie求出其真实值,操作之后逆置换回去。

总的时间复杂度是 O ( n lg ⁡ n ) O(n \lg n) O(nlgn)


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 300010
#define M 500010
#define uint unsigned
#define bit 32
uint input(){
	char ch=getchar();
	while (ch<'0' || ch>'9')
		ch=getchar();
	uint x=0;
	do{
		x=x*10+ch-'0';
		ch=getchar();
	}
	while ('0'<=ch && ch<='9');
	return x;
}
uint output(uint x){
	static uint st[20];
	if (x==0)
		putchar('0');
	else{
		int k=0;
		for (;x;x/=10)
			st[++k]=x%10;
		for (;k;--k)
			putchar('0'+st[k]);
	}
}
int n,m,B;
uint a[N];
struct EDGE{
	int to;
	EDGE *las;
} e[N*4];
int ne;
EDGE *last[N];
int fa[N];
void init(int x){
	for (EDGE *ei=last[x];ei;ei=ei->las)
		if (ei->to!=fa[x])
			fa[ei->to]=x,init(ei->to);
}
//(False) a[x] -> getp(rt[fa[x]],a[x]) (True)
struct Node *null;
Node *newnode();
struct Node{
	Node *c[2];
	bool isrev;
	uint tag;
	void rse(){
		swap(c[0],c[1]);
		isrev^=1;
	}
	void gt(uint _tag){tag^=_tag;}
	void pd(){
		if (tag&1)
			rse();
		if (tag>>1){
			if (c[0]==null) c[0]=newnode();c[0]->gt(tag>>1);
			if (c[1]==null) c[1]=newnode();c[1]->gt(tag>>1);
		}
		tag=0;
	}
} d[M*70],*rt[N];
int cnt;
Node *newnode(){return &(d[++cnt]={null,null,0});}
uint getp(Node *t,uint x){
	uint y=0;
	for (int i=0;i<bit;++i){
		t->pd();
		uint w=x>>i&1;
		y|=(w^t->isrev)<<i;
		t=t->c[w^t->isrev];
	}
	return y;
}
uint getp_rev(Node *t,uint x){
	uint y=0;
	for (int i=0;i<bit;++i){
		t->pd();
		uint w=x>>i&1;
		y|=(w^t->isrev)<<i;
		t=t->c[w];
	}
	return y;
}
void trans_add(Node *t){
	for (int i=0;i<bit;++i){
		t->pd();
		t->rse();
		if (t->c[0]==null)
			t->c[0]=newnode();
		t=t->c[0];
	}
}
void trans_xo(Node *t,int v){
	t->gt(v);
}
void add(int x){
	uint b=getp(rt[fa[x]],a[x]);
	b++;
	a[x]=getp_rev(rt[fa[x]],b);
}
void xo(int x,uint v){
	uint b=getp(rt[fa[x]],a[x]);
	b^=v;
	a[x]=getp_rev(rt[fa[x]],b);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout); 
	n=input(),m=input();
	for (int i=1;i<=n;++i)
		a[i]=input();
	for (int i=1;i<n;++i){
		int u=input(),v=input();
		e[ne]={v,last[u]};
		last[u]=e+ne++;
		e[ne]={u,last[v]};
		last[v]=e+ne++;
	}
	init(1);
	null=d;
	*null={null,null,0};
	for (int i=0;i<=n;++i)
		rt[i]=newnode();
	for (int i=1;i<=m;++i){
		uint op=input(),x=input();
		if (op==1)
			output(getp(rt[fa[x]],a[x])),putchar('\n');
		else if (op==2){
			if (fa[x])
				add(fa[x]);
			trans_add(rt[x]);
		}
		else{
			uint v=input();
			if (fa[x])
				xo(fa[x],v);
			trans_xo(rt[x],v);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值