题意:
给你一棵树,每个点原本都有一个权值
w
i
w_i
wi,但是你只知道相邻两个点之间的
w
u
⊕
w
v
w_u\oplus w_v
wu⊕wv,问你有多少种
w
1
,
2
,
.
.
.
,
n
w_{1,2,...,n}
w1,2,...,n
n
≤
1
e
5
,
w
i
<
2
30
n\le1e5,w_i<2^{30}
n≤1e5,wi<230
思路:
显然我们如果确定了一个点的权值,那么其他的点都就确定了。
更具体的是,我们可以求出
1
1
1到其他点之间的
w
1
⊕
w
x
w_1\oplus w_x
w1⊕wx,其中我们可以假设
w
1
=
0
w_1=0
w1=0,那么当给
w
1
⊕
a
w_1\oplus a
w1⊕a时,就相当于将其他
w
2
,
3
,
.
.
.
,
n
⊕
a
w_{2,3,...,n}\oplus a
w2,3,...,n⊕a,所以我们问题就转换成了对于每个区间求合法的
a
a
a使得
l
i
≤
w
i
⊕
a
≤
r
i
l_i\le w_i\oplus a\le r_i
li≤wi⊕a≤ri,转换一下就是
w
i
⊕
[
l
i
,
r
i
]
w_i\oplus [l_i,r_i]
wi⊕[li,ri]的合法区间。这个区间肯定不是连续的,所以考虑将其拆分成若干区间。
由于拆分的区间肯定不能很多,所以考虑用二进制来拆分这个区间。考虑这样一种形式的二进制区间
[
x
x
x
x
0000
,
x
x
x
x
1111
]
[xxxx0000,xxxx1111]
[xxxx0000,xxxx1111],其中
x
x
x表示可以是任意数,但是两个的
x
x
x对应位置必须相同。这样的区间满足其异或上
w
i
w_i
wi仍是一段连续的区间。我们惊奇的发现,这个区间后半部分不正是线段树的每段区间吗?所以考虑建一棵
[
0
,
2
30
−
1
]
[0,2^{30}-1]
[0,230−1]的线段树,将这个区间插入,可知最多能被分成
l
o
g
n
logn
logn段区间。当这个区间被完全包含的时候,我们打一个懒标记即可。
查询的时候如果懒标记
=
n
=n
=n的时候,就直接返回区间长度即可。
// Problem: Tree Xor
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/11255/E
// Memory Limit: 524288 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;
//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=5000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,w[N],p[N];
int l[N],r[N],idx;
struct Node {
int l,r;
int cnt,lazy;
}tr[N<<2];
vector<PII>v[N];
void dfs(int u,int fa) {
for(auto x:v[u]) {
if(x.X==fa) continue;
w[x.X]=w[u]^x.Y;
dfs(x.X,u);
}
}
int newnode() {
int u=++idx;
tr[u].l=tr[u].r=0;
tr[u].cnt=tr[u].lazy=0;
return u;
}
void insert(int &u,int l,int r,int ql,int qr,int w,int dep) {
if(!u) u=newnode();
int nl=(l^w)&(((1<<30)-1)^((1<<(dep+1))-1));
int nr=(l^w)|((1<<(dep+1))-1);
if(nl>=ql&&nr<=qr) {
tr[u].lazy++;
tr[u].cnt++;
return;
}
if(tr[u].cnt) {
if(!tr[u].l) tr[u].l=newnode();
if(!tr[u].r) tr[u].r=newnode();
tr[tr[u].l].cnt+=tr[u].cnt; tr[tr[u].r].cnt+=tr[u].cnt;
tr[u].cnt=0;
}
int mid=(l+r)>>1;
int ll=(l^w)&(((1<<30)-1)^((1<<(dep))-1)),lr=(l^w)|((1<<(dep))-1);
int rl=(r^w)&(((1<<30)-1)^((1<<(dep))-1)),rr=(r^w)|((1<<(dep))-1);
if(w>>dep&1) {
if(ql<=rr) insert(tr[u].r,mid+1,r,ql,qr,w,dep-1);
if(qr>=ll) insert(tr[u].l,l,mid,ql,qr,w,dep-1);
} else {
if(ql<=lr) insert(tr[u].l,l,mid,ql,qr,w,dep-1);
if(qr>=rl) insert(tr[u].r,mid+1,r,ql,qr,w,dep-1);
}
}
int query(int u,int l,int r) {
if(!u) return 0;
if(tr[u].cnt==n) return r-l+1;
if(tr[u].cnt) {
if(tr[u].l) tr[tr[u].l].cnt+=tr[u].cnt;
if(tr[u].r) tr[tr[u].r].cnt+=tr[u].cnt;
tr[u].cnt=0;
}
int ans=0,mid=(l+r)>>1;
ans+=query(tr[u].l,l,mid);
ans+=query(tr[u].r,mid+1,r);
return ans;
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&l[i],&r[i]);
for(int i=1;i<=n-1;i++) {
int a,b,c; scanf("%d%d%d",&a,&b,&c);
v[a].pb({b,c}); v[b].pb({a,c});
}
dfs(1,0);
int rt=newnode();
int limit=(1<<30)-1;
for(int i=1;i<=n;i++) {
insert(rt,0,limit,l[i],r[i],w[i],29);
}
printf("%d\n",query(1,0,limit));
return 0;
}
/*
*/