牛客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<<(k−1),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;
}