牛客2021暑期训练4-E-Tree Xor

牛客2021暑期训练4-E-Tree Xor

题目链接

题意

给定一棵树,节点有权值 w [ i ] w[i] w[i] w [ i ] ∈ [ l i , r i ] w[i]\in[l_i,r_i] w[i][li,ri],相连节点权值异或已知,求满足的权值组数

题解

先令 w [ 1 ] = 0 w[1]=0 w[1]=0, 解出剩下的 w w w,之后可以发现, 如果要 w [ 1 ] = a w[1]=a w[1]=a, 那么剩下的 w w w 都会 x o r xor xor a a a

所以就变成了求解合法的 a a a 的数量, 限制有 n n n 个不等式, 形式为 l [ i ] < = w [ i ] x o r a < = r [ i ] l[i]<=w[i] xor a<=r[i] l[i]<=w[i]xora<=r[i]

因此 a a a 的取值也就相当于 [ l i , r i ] [l_i,r_i] [li,ri] 中每个数都和 w [ i ] w[i] w[i] x o r xor xor,然而区间中每个数和 w [ i ] w[i] w[i] 异或得到的区间是不连续的,所以关键就是将 [ l i , r i ] [l_i,r_i] [li,ri] 分成若干区间使得每个区间中的数和 w [ i ] w[i] w[i] 异或得到的值仍然是连续的

[ 8 , 15 ] − > [ . . . 001000 , . . . 001111 ] 2 x o r X [8,15]->[...001000,...001111]_2 xor X [8,15]>[...001000,...001111]2xorX 可以将 X X X 分为前一部分异或和后 3 3 3 位异或,可以知道,前一部分异或得到的是不变的,后 3 3 3 位异或得到的一定是不同的,也就是说,后 3 3 3 位仍然会是 000 , 001 , 010 , . . . , 111 000,001,010,...,111 000,001,010,...,111,结合代码、自己写写加深理解

所以对 [ l i , r i ] [l_i,r_i] [li,ri] 将它分成的区间就是这种形式的 ( 1 < < ( k − 1 ) , 1 < < k ) (1<<(k-1),1<<k) (1<<(k1),1<<k)

异或后,一个区间会分成不相交的若干区间,差分求并

具体见代码

代码
#include<bits/stdc++.h>
#define PR pair<int,int>
#define Fi first
#define Se second
#define Mp make_pair
#define Pb push_back
using namespace std;
const int N=1e5+9;
int n;
int L[N],R[N],W[N];
vector<PR>e[N],sol,ans;
void dfs(int u,int fa) //求当w1=0时,各节点的w 
{
	for(int i=0;i<e[u].size();i++)
	{
		int v=e[u][i].Fi,w=e[u][i].Se;
		if(v==fa) continue;
		W[v]=W[u]^w;
		dfs(v,u);
	}
}
void Putin(int l,int r,int k,int z)
{
	int tz=z&(((1<<30)-1)^((1<<k)-1)); //取出z大于k位的1 
	sol.Pb(Mp(l^tz,(l^tz)+(1<<k)-1)); //[l,r]^z后为[l^z,l^z+len] 
}
void Work(int l,int r,int k,int x,int y,int z)
{
	if(x<=l&&r<=y)
	{
		Putin(l,r,k,z);
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) Work(l,mid,k-1,x,y,z);
	if(y>mid) Work(mid+1,r,k-1,x,y,z);
}
int Solve() //求区间并 
{
	for(int i=0;i<sol.size();i++) //用的差分 
	{
		ans.Pb(Mp(sol[i].Fi,1));
		ans.Pb(Mp(sol[i].Se+1,-1));
	}
	sort(ans.begin(),ans.end());
	//一个区间xor后分成互不相交的几个区间,覆盖为n时必然有以下关系 
	int tot=0,cnt=0;
	for(int i=0;i<ans.size();i++)
	{
		cnt+=ans[i].Se;
		if(cnt==n) tot+=ans[i+1].Fi-ans[i].Fi;
	}
	return tot;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&L[i],&R[i]);
	for(int i=1;i<n;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		e[u].Pb(Mp(v,w));
		e[v].Pb(Mp(u,v));
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)
		Work(0,(1<<30)-1,30,L[i],R[i],W[i]);
	printf("%d\n",Solve());
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值