题意
给你n节点的树,每个节点的权值范围Li ≤ \leq ≤Wi ≤ \leq ≤Ri ,然后再给你n-1条边,边权定义为u,v节点权值的异或值。
现在问你,满足条件的W的取值有多少组。
思路
根据异或的性质,知二求一,那么对于这颗树,只要一个节点的权值确定,那么别的顶点的权值,跑一个dfs就可以求出来。应为
边权是已知的。
不妨让1号节点的权值为0,,那么可以预处理出别的节点的值W’i ,如果1号节点的取值为a,那么每个Wi=W’i ⨁ \bigoplus ⨁a。
由于题目对每个Wi是有限制,可以反推出来Li ≤ \leq ≤W’i ⨁ \bigoplus ⨁a ≤ \leq ≤Ri,进一步得出a的区间为[Li , Ri ] ⨁ \bigoplus ⨁W’i.
那么问题就转换为求n个区间[Li , Ri ] ⨁ \bigoplus ⨁W’i的交集。即是问题所求的答案。
问题1.如何求[Li , Ri ] ⨁ \bigoplus ⨁W’i
因为一个连续的区间异或完以后,不一定是一个连续区间,所以我们要先把区间划分成log(w)个连续的区间。
对于每个划分的区间,2 b是他们的区间长度,它们L二进制的最后b位一定为0,R二进制的最后b位一定为1,其余位相同。这样他们异或任何一个数,
区间都是连续的。并且新区间的左端点L是旧区间左端点L的除去最后b位异或上W的除去最后b位。由于区间长度是b,得新区间有右端点R=L+(1<<b)-1。
题目给定的区间是0 ≤ \leq ≤Li ≤ \leq ≤ ≤ \leq ≤Ri <230,因此可以用[0,230-1]的线段树来维护这个区间。
问题2.区间求交
对于n个区间求交,并且每个区间不一定是连续的。可以这样处理
对于每一段连续的区间,把他们起点和终点的下标放到一个PII里面,如果是起点,标记为-1,如果是终点标记为1,然后排序。
扫描一遍,维护一个dep,一开始为0,每次dep+=-it.second;如果dep为n,说明是n个区间的交集。加上这个区间的长度。
图片上是对俩个区间求交,方法是用上述过程。俩个结合起来看。
源代码
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ll long long
#define INF 0x7f7f7f7f
#define endl '\n'
#define mem(a, b) memset(a, b, sizeof(a))
#define open freopen("ii.txt", "r", stdin)
#define close freopen("oo.txt", "w", stdout)
#define IO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)
#define pb push_back
typedef pair<ll, ll> PII;
const ll N = 1e5 + 10;
const double PI = 3.1415926535898;
const ll mod = 1e9 + 7;
using namespace std;
ll l[N],r[N];
ll w[N];
vector<PII>e[N];
vector<PII>p;
int n;
void dfs(int x,int fa)
{
for(auto it:e[x])
{
if(it.first==fa)continue;
w[it.first]=w[x]^it.second;
dfs(it.first,x);
}
}
void init()
{
w[1]=0;
dfs(1,0);
}
void findd(ll l,ll r,ll w,ll b)
{
ll prew=w>>b;
prew=prew<<b;
ll prel=l>>b;
prel=prel<<b;
l=prew^prel;
r=l+(1<<b)-1;
p.pb(PII(l,-1));//记录端点的下标,起点标记为-1,终点标记为1,如果俩个下标相同的话,终点在后面,因为后面要排序,根据PII的优先级,分别标记为-1,和1,
p.pb(PII(r,1));
//cout<<l<<" "<<r<<endl;
}
void work(ll l,ll r,ll nl,ll nr,ll w,ll b)
{
if(nl<=l&&r<=nr)
{
findd(l,r,w,b);
return;
}
ll mid=(l+r)>>1;
if(nl<=mid)work(l,mid,nl,nr,w,b-1);
if(nr>mid)work(mid+1,r,nl,nr,w,b-1);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>l[i]>>r[i];
}
for(int i=0;i<n-1;i++)
{
ll u,v,w;
cin>>u>>v>>w;
e[u].pb(PII(v,w));//vector存无向边
e[v].pb(PII(u,w));
}
init();
//for(int i=1;i<=n;i++)cout<<w[i]<<" ";
for(int i=1;i<=n;i++)
{
work(0,(1<<30)-1,l[i],r[i],w[i],30);//划分每个区间,并且求出新区间。
}
sort(p.begin(),p.end());
ll cnt=0;
ll ans=0;
for(int i=0;i<p.size();i++)//区间求交
{
cnt+=-p[i].second;
if(cnt==n)
{
ans+=p[i+1].first-p[i].first+1;
}
}
cout<<ans<<endl;
return 0;
}