The 2019 ACM-ICPC China Shannxi Provincial Programming Contest E.Tree(树链剖分+线段树区间or/and/xor)

题目

n(n<=1e5)个数,第i个数为ai(0<=ai<=1e9),代表树的点权

以下n-1行读入树的双向边u和v

以下m(m<=1e5)个操作,每次操作读入op,s,t,操作分三种类型

1 s t 将树上1到s的路径的点权v都作v|t操作

2 s t 将树上1到s的路径的点权v都作v&t操作

3 s t 将树上1到s的路径的点权都取出来,每一个值当一堆石子,再加入一堆数量为t的石子

Ming先手,和对方用这些石子玩Nim游戏,问Ming是否必胜,玩完之后除了t那堆不放以外其余石子放回

题解

标重儿子,预处理dfs序,拍到线段树上,裸的树剖操作,

按位开线段树,每一位开一个标记,|1、&0的时候更新标记,其余时候不管

根据nim游戏,将1到s的路径都作异或操作,最后再异或t,如果非0则必胜,否则必败

询问的时候,询问第i位对应的线段树对应的区间[ql,qr]内的1的数量,

若为奇数,说明异或结果为1,则加上1ll<<i,否则不管

最后询问的结果非0则必胜,否则必败

心得

单组数据,所以线段树那里没有init操作

其实就是很裸的树剖,只是我的线段树写法,常数好像有点大

不过这题不卡常哈,开了4s,程序2s过了

还是喜欢点权树剖,边权树剖把边权收进远端儿子,查询的时候还得减去lca的值,比较麻烦

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
struct edge
{
	int to,nex;
}e[maxn*2];  
struct node
{
	int cs[32],cov[32];
	node()
	{
		for(int i=30;i>=0;--i)
		cs[i]=0,cov[i]=-1;
	}
}tree[maxn*5];
//par[]:当前点的父亲
//dep[]:当前点的深度(以根为0) 
//siz[]:当前点的子树大小(包括自己)
//son[]:当前点的重儿子(siz最大的那个儿子)
//id[]:dfs序时间戳 
//arc[]:id[]的映射,如id[u]=v,则arc[v]=u 
//top[]:某一条链最靠近树根的结点 即边最靠上的那个端点 
//tree[]:按照dfs序建的线段树 重链映射到连续区间 轻链映射到一个点 
//cnt:edge的标号;num:时间戳的标号
int par[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn];
int head[maxn],cnt,a[maxn];
int id[maxn],arc[maxn],num;
int n,m,op;
int s,t;
void add(int u,int v)
{
	e[cnt].to=v;
	e[cnt].nex=head[u];
	head[u]=cnt++;
}
void init()
{
	//memset(par,0,sizeof(par)); 由于dfs1的时候会覆盖 不需要 
	//memset(dep,0,sizeof(dep)); 由于后续dep[v]=dep[u]+1 只需更新根节点 
	//memset(siz,0,sizeof(siz)) 由于siz[u]=1覆盖 不需要 
	//memset(tree,0,sizeof(tree));
	//memset(top,0,sizeof(top)); 后续都是根据根节点更新的 
	//memset(id,0,sizeof(id)); num=0了什么都好说 
	dep[1]=0;top[1]=1;
	memset(son,0,sizeof(son));
	memset(head,-1,sizeof(head));
	cnt=num=0; 
	
}
void dfs1(int u)
{
	siz[u]=1;
	for(int i=head[u];~i;i=e[i].nex)
	{
		int v=e[i].to;
		if(v!=par[u])
		{
			par[v]=u;
			dep[v]=dep[u]+1;
			dfs1(v);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])son[u]=v;
		}
	}
}
void dfs2(int u)
{
	id[u]=++num;//dfs序 时间戳 id[i]即在线段树的第i位 想象给一颗dfs树标号 
	arc[id[u]]=u;//arc[i] 第i位的值对应的原节点 
	if(son[u])//优先遍历重儿子 保证重链dfs序相邻 
	{
		top[son[u]]=top[u];//重链上的根一定和父相同
		dfs2(son[u]); 
	} 
	for(int i=head[u];~i;i=e[i].nex)
	{
		int v=e[i].to;
		if(v!=par[u]&&v!=son[u])//非父 轻儿子
		{
			top[v]=v;//轻链以自己为根
			dfs2(v); 
		} 
	}
}
void pushup(int p)
{
	for(int i=30;i>=0;--i)
	tree[p].cs[i]=(tree[p<<1].cs[i]+tree[p<<1|1].cs[i])%2;
}
void get_new(int p,int l,int r,int op,int v)
{
	for(int i=30;i>=0;--i)
	{
		if(op==1&&(v>>i&1))tree[p].cs[i]=(r-l+1)%2;
		if(op==2&&!(v>>i&1))tree[p].cs[i]=0; 
	}
}
void build(int p,int l,int r)
{
	if(l==r)
	{
		for(int i=30;i>=0;--i)
		if(a[arc[l]]>>i&1)tree[p].cs[i]=1;
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(p);
} 
void pushdown(int i,int p,int l,int r)
{
	if(~tree[p].cov[i])
	{
		int mid=(l+r)/2;
		if(tree[p].cov[i]==1)
		{
			tree[p<<1].cs[i]=(mid-l+1)%2;
			tree[p<<1|1].cs[i]=(r-mid)%2;
		}
		else
		{
			tree[p<<1].cs[i]=0;
			tree[p<<1|1].cs[i]=0;
		}
		tree[p<<1].cov[i]=tree[p].cov[i];
		tree[p<<1|1].cov[i]=tree[p].cov[i];
		tree[p].cov[i]=-1;
	}
}
void update(int p,int l,int r,int ql,int qr,int op,int v)
{
	if(ql<=l&&r<=qr)
	{
		for(int i=30;i>=0;--i)
		{
			if(op==1&&(v>>i&1))
			{
			 tree[p].cs[i]=(r-l+1)%2;
			 tree[p].cov[i]=1;
		    }
			if(op==2&&!(v>>i&1))
			{
			 tree[p].cs[i]=0;
			 tree[p].cov[i]=0;
		    }
		}
		return; 
	}
	for(int i=30;i>=0;--i)
	pushdown(i,p,l,r); 
	int mid=(l+r)>>1;
	if(ql<=mid)update(p<<1,l,mid,ql,qr,op,v);
	if(qr>mid)update(p<<1|1,mid+1,r,ql,qr,op,v);
	pushup(p);
} 
ll aski(int i,int p,int l,int r,int ql,int qr)//第i位的个数 
{
	 if(ql<=l&&r<=qr)return tree[p].cs[i]%2;
	 pushdown(i,p,l,r);
	 ll res=0; 
	 int mid=(l+r)>>1;
	 if(ql<=mid)(res+=aski(i,p<<1,l,mid,ql,qr))%2;
	 if(qr>mid)(res+=aski(i,p<<1|1,mid+1,r,ql,qr))%2;
	 return res;
}
ll ask(int p,int l,int r,int ql,int qr)//区间异或 
{
	 ll res=0;
	 for(int i=30;i>=0;--i)
	 {
	 	ll num=aski(i,p,l,r,ql,qr);
	 	if(num&1)res+=(1ll<<i);
	 }
	 return res;
}
void change(int u,int v,int op,int t)//u->v链上的最大值 按轻重链找 
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		update(1,1,n,id[top[u]],id[u],op,t);//重链区间和或轻链单点和 
		u=par[top[u]]; 
	}
	//u、v现在top相同 在一条链上 
	if(dep[u]>dep[v])update(1,1,n,id[v],id[u],op,t);
	else update(1,1,n,id[u],id[v],op,t);
} 
ll findxor(int u,int v)//u->v链上的最大值 按轻重链找 
{
	ll ans=0;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		ans=ans^ask(1,1,n,id[top[u]],id[u]);//重链区间和或轻链单点和 
		u=par[top[u]]; 
	}
	//u、v现在top相同 在一条链上 
	if(dep[u]>dep[v])ans=ans^ask(1,1,n,id[v],id[u]);
	else ans=ans^ask(1,1,n,id[u],id[v]);
	return ans; 
} 
int main() 
{
	//freopen("test.in","r",stdin);
	//freopen("test.out","w",stdout); 
	 scanf("%d%d",&n,&m);
	 init();
     for(int i=1;i<=n;++i)
	 scanf("%d",&a[i]);//点上有点权的情况
	 for(int i=1;i<n;++i)//树 n-1条边 边权
	 {
	 	int u,v;
	 	scanf("%d%d",&u,&v);
	 	add(u,v);
	 	add(v,u);
	 }
	 dfs1(1);
	 dfs2(1);
	 build(1,1,n);
	 for(int i=1;i<=m;++i)
	 {
	 	scanf("%d%d%d",&op,&s,&t);
	 	if(op==3)puts((findxor(1,s)^t)?"YES":"NO");
	 	else change(1,s,op,t);
	 }
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值