E-Tree Xor
首先不考虑区间限制条件,我们给定其中一个点的权值后,那么其他点的权值也就确定。比如 val 1 = 0 \text{val}_1=0 val1=0,即可通过变得限制求出其他点 val u \text{val}_u valu,而且不难发现如果 val 1 = 0 ⊕ a \text{val}_1=0\oplus a val1=0⊕a那么其余点的权值 val u = 0 ⊕ a \text{val}_u=0\oplus a valu=0⊕a
下面记 val u \text{val}_u valu为当 val 1 = 0 \text{val}_1=0 val1=0时,u点的权值。
于是问题转化为求出满足下面区间限制
a
a
a的个数。
L
1
≤
val
1
⊕
a
≤
R
1
L
2
≤
val
2
⊕
a
≤
R
2
…
L
n
≤
val
n
⊕
a
≤
R
n
\text L_1\leq \text{val}_1\oplus a\leq \text R_1\\\text L_2\leq \text{val}_2\oplus a\leq \text R_2\\ \dots\\ \text L_n\leq \text{val}_n\oplus a\leq \text R_n
L1≤val1⊕a≤R1L2≤val2⊕a≤R2…Ln≤valn⊕a≤Rn
考虑其中一种限制直观的想法是 L u ≤ val u ⊕ a ≤ R u ⇒ L 1 ⊕ val 1 ≤ a ≤ R 1 ⊕ val 1 \text L_u\leq \text{val}_u\oplus a\leq \text R_u\Rightarrow \text L_1\oplus \text{val}_1\ \leq a\leq \text R_1\oplus \text{val}_1 Lu≤valu⊕a≤Ru⇒L1⊕val1 ≤a≤R1⊕val1不难发现上面转化是错误的,而且可以发现 a a a的区间是不连续的!!!需要找出这些不连续的区间!
于是就很难处理,后面就是讲题人的做法(真滴强)
我们可以利用 [ 0 , 2 30 − 1 ] [0,2^{30}-1] [0,230−1] 的线段树, 把 [ L i , R i ] [L_i , R_i] [Li,Ri] 分成 log W \log W logW个连续的区间, 且每个区间的形式是 : [ ∗ ∗ ∗ 00 … 00 , ∗ ∗ ∗ 11 … 11 ] [***00\dots00,***11\dots 11] [∗∗∗00…00,∗∗∗11…11], 这样的区间异或上 val i \text{val}_i vali 后仍然还是一个区间
不妨设 ∗ ∗ ∗ *** ∗∗∗的数量是k
不难发现区间
[
∗
∗
∗
00
…
00
,
∗
∗
∗
11
…
11
]
[***00\dots00,***11\dots 11]
[∗∗∗00…00,∗∗∗11…11]抑或上
val
i
\text{val}_i
vali的区间是
[
−
−
−
00
…
00
,
−
−
−
,
11
…
11
]
[- - -00\dots00,---,11\dots11]
[−−−00…00,−−−,11…11]
其中
−
−
−
---
−−−是
(
∗
∗
∗
00
…
00
)
⊕
val
i
(***00\dots00)\oplus \text{val}_i
(∗∗∗00…00)⊕vali的前
k
k
k位的值
比如:
[
1010
0000
,
1010
1111
]
[\color{Blue}{1010} \color{Red}{0000},\color{Blue}{1010} \color{Red}{1111}]
[10100000,10101111]
⊕
1001
1010
⇒
\oplus\color{Blue}{1001} \color{Red}{1010}\Rightarrow
⊕10011010⇒
[
0011
0000
,
0011
1111
]
[\color{Blue}{0011} \color{Red}{0000},\color{Blue}{0011} \color{Red}{1111}]
[00110000,00111111]
蓝色部分异或 0011 = 1010 ⊕ 0011 \color{Blue}0011=1010\oplus0011 0011=1010⊕0011原红色部分不变,稍微思考一下就知道为什么了。
通过上面操作成功找出这些不连续的区间!!!
然后就sort区间差分乱搞就行。时间复杂度2log,可以用线段树维护所有不合法区间的并集,把不合法的区间标记为1,把时间复杂度降为1log
Code1
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
T res=0;T fg=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
return res*fg;
}
const int N=100010;
int h[N],e[2*N],ne[2*N],w[2*N],idx;
void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}
int L[N],R[N];
int val[N];
struct node
{
int l,r;
}tree[N*40];
int rt,cnt,n;
vector<pair<int,int>> vec;
void insert(int &u,int l,int r,int L,int R,int val)
{
if(!u) u=++cnt;
if(L<=l&&r<=R)
{
int ql=l^(val&(~(r-l)));//异或之后的区间[ql,qr]
int qr=ql+r-l;
vec.push_back({ql,1});
vec.push_back({qr+1,-1});
return;
}
int mid=l+r>>1;
if(L<=mid)
insert(tree[u].l,l,mid,L,R,val);
if(R>mid)
insert(tree[u].r,mid+1,r,L,R,val);
}
void dfs(int u,int fa)
{
insert(rt,0,(1<<30)-1,L[u],R[u],val[u]);
for(int i=h[u];i!=-1;i=ne[i])
{
int v=e[i];
if(v==fa) continue;
val[v]=val[u]^w[i];
dfs(v,u);
}
}
int solve()
{
sort(vec.begin(),vec.end());
vec.push_back({1<<30,0});
int ans=0;
int cur=0;
for(int i=0;i<vec.size()-1;i++)
{
cur+=vec[i].second;
if(cur==n) ans+=vec[i+1].first-vec[i].first;
}
return ans;
}
int main()
{
n=rd();
for(int i=1;i<=n;i++) L[i]=rd(),R[i]=rd();
memset(h,-1,sizeof h);
for(int i=1;i<n;i++)
{
int u=rd(),v=rd(),w=rd();
add(u,v,w),add(v,u,w);
}
dfs(1,0);
printf("%d\n",solve());
}
比较容易想到的做法就是我们需要求
L
1
≤
val
1
⊕
a
≤
R
1
L
2
≤
val
2
⊕
a
≤
R
2
…
L
n
≤
val
n
⊕
a
≤
R
n
\text L_1\leq \text{val}_1\oplus a\leq \text R_1\\\text L_2\leq \text{val}_2\oplus a\leq \text R_2\\ \dots\\ \text L_n\leq \text{val}_n\oplus a\leq \text R_n
L1≤val1⊕a≤R1L2≤val2⊕a≤R2…Ln≤valn⊕a≤Rn
转化成
[
val
1
⊕
a
≤
R
1
]
➖
[
val
1
⊕
a
<
L
1
]
[
val
2
⊕
a
≤
R
2
]
➖
[
val
2
⊕
a
<
L
2
]
…
[
val
n
⊕
a
≤
R
n
]
➖
[
val
n
⊕
a
<
L
n
]
[\text{val}_1\oplus a\leq \text R_1]➖[\text{val}_1\oplus a< \text L_1]\\ [\text{val}_2\oplus a\leq \text R_2]➖[\text{val}_2\oplus a< \text L_2]\\ \dots\\ [\text{val}_n\oplus a\leq \text R_n]➖[\text{val}_n\oplus a< \text L_n]
[val1⊕a≤R1]➖[val1⊕a<L1][val2⊕a≤R2]➖[val2⊕a<L2]…[valn⊕a≤Rn]➖[valn⊕a<Ln]
上面的➖理解为两个集合相减。
实际上我需要求的就是 ⊕ a ≤ b \oplus a\leq b ⊕a≤b的区间,显然字典树可以做,相当于求 ⊕ a ≤ b \oplus a\leq b ⊕a≤b的数有哪些,和第二次杭电I love counting求的步骤一样,在Trie树上讨论一下就行。
Code2
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
T res=0;T fg=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
return res*fg;
}
const int N=100010;
int h[N],e[2*N],ne[2*N],w[2*N],idx;
void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}
int L[N],R[N];
int val[N];
struct node
{
int l,r;
}tree[N*40];
int rt,cnt,n;
vector<pair<int,int>> seg[2];
void query(int k,int a,int b)// x^a<=b
{
int u=rt;
int cur=0;
for(int i=29;i>=0;i--)//30位
{
int ai=a>>i&1;
int bi=b>>i&1;
if(bi==1) //b=1
{
if(ai==0)
{
seg[k].push_back({cur,cur+(1<<i)-1});
cur+=1<<i;
if(!tree[u].r) tree[u].r=++cnt;
u=tree[u].r;
}
else
{
seg[k].push_back({cur+(1<<i),cur+(1<<i)+(1<<i)-1});
if(!tree[u].l) tree[u].l=++cnt;
u=tree[u].l;
}
}
else
{
if(ai==0)
{
if(!tree[u].l) tree[u].l=++cnt;
u=tree[u].l;
}
else
{
cur+=1<<i;
if(!tree[u].r) tree[u].r=++cnt;
u=tree[u].r;
}
}
}
seg[k].push_back({cur,cur});
}
void dfs(int u,int fa)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int v=e[i];
if(v==fa) continue;
val[v]=val[u]^w[i];
dfs(v,u);
}
}
int solve()
{
vector<pair<int,int>>vec;
for(auto t:seg[0])
{
vec.push_back({t.first,-1});
vec.push_back({t.second+1,+1});
}
for(auto t:seg[1])
{
vec.push_back({t.first,+1});
vec.push_back({t.second+1,-1});
}
sort(vec.begin(),vec.end());
vec.push_back({1<<30,0});
int ans=0;
int cur=0;
for(int i=0;i<vec.size()-1;i++)
{
cur+=vec[i].second;
if(cur==n) ans+=vec[i+1].first-vec[i].first;
}
return ans;
}
int main()
{
n=rd();
for(int i=1;i<=n;i++) L[i]=rd(),R[i]=rd();
memset(h,-1,sizeof h);
for(int i=1;i<n;i++)
{
int u=rd(),v=rd(),w=rd();
add(u,v,w),add(v,u,w);
}
dfs(1,0);
for(int i=1;i<=n;i++)
{
if(L[i]>0) query(0,val[i],L[i]-1);
query(1,val[i],R[i]);
}
printf("%d\n",solve());
}
其实仔细分析应该能写出Trie树的做法,不过当时没有分析出来啊www
要加油哦~