P7156 [USACO20DEC] Cowmistry P 题解
给一个代码好写的做法。
首先假设建了完整的 Trie 树,那么三个不同的点
那么容易发现最大异或值一定出现在
先找
如果当前位走
-
-
1. $S$ 和 $T$ 这一位选不一样(即 $u \to o, w \to o \oplus 1$):此时异或值为 1,等于 $K$ 的当前位,限制仍然没有解除(紧贴限制),$r \leftarrow b$。 2. $S$ 和 $T$ 这一位选一样(即 $u \to o, w \to o$):此时异或值为 0,小于 $K$ 的当前位 1,$w$ 在这一位解除了限制,后续可以随便选,所以 $r \leftarrow r + \text{siz}_{S_o} \times \text{siz}_{T_{o}}$。
接下来计算答案。
第一种是
-
-
1. $T$ 出分叉出 $x$,也就是 $v$ 在这一位解除限制。此时 $v \in T_o, w \in T_{o\oplus 1}$。只需要满足 $u\oplus w\leq K$ 即可(即 $b$ 部分),$v$ 可以在 $T_o$ 中任选,所以贡献 $b \times \text{siz}_{T_o}$。 2. $v, w$ 都在这一位相对于 $u$ 解除了限制(即 $u \to o$,而 $v, w$ 都去往 $T_o$)。此时 $u,v,w$ 满足 $u \oplus v < K$ 且 $u \oplus w < K$。$v, w$ 在 $T_o$ 子树中任意分叉均可,所以贡献 $\binom{\text{siz}_{T_o}}{2} \times \text{siz}_{S_o}$。
最后算一下
但是节点数量太多了,考虑把满二叉树都压缩一下,然后记忆化
#include<bits/stdc++.h>
using u32=unsigned int;
using u64=unsigned long long;
const u32 mod=1000'000'007;
const u32 iv2=(mod+1)/2;
u32 K;
const int W=29;
const int V=1<<(W+1);
const int N=100000010;
int trie[N][2],cntnod=0,rt;
u32 siz[N];
std::map<u64,std::array<u32,2>> mp;
std::array<u32,2> solve(int S,int T,char flg,int dep){
if(dep==-1) return {!!T,0};
u32 k=(K>>dep)&1,o;
u32 r=0,a=0,ss;
u64 hash=(u64(S+1)<<40)|(u64(T+1)<<8)|(dep<<1)|flg;
auto it=mp.find(hash);
if(it!=mp.end()) return it->second;
auto sz=[&](int p){
if(~p) return siz[p];
return 1u<<dep;
};
auto ch=[&](int p,int o){
if(~p) return trie[p][o];
return -1;
};
if(k==0){
for(o=0;o<2;++o) if(ch(S,o)){
auto [b,y]=solve(ch(S,o),ch(T,o),flg,dep-1);
(r+=b)%=mod;
(a+=y)%=mod;
}
}else{
for(o=0;o<2;++o)if(ch(S,o)){
auto [b,y]=solve(ch(S,o),ch(T,o^1),1,dep-1);
r=(r+b+(u64)sz(ch(S,o))*sz(ch(T,o)))%mod;
(a+=y)%=mod;
if(flg){
ss=sz(ch(T,o));
a=(a+(u64)b*ss+(u64)ss*(ss-1)%mod*iv2%mod*sz(ch(S,o)))%mod;
}
}
}
if(flg) for(o=0;o<2;++o){
ss=sz(ch(S,o^1));
a=(a+(u64)ss*(ss-1)%mod*iv2%mod*sz(ch(S,o)))%mod;
}
return mp[hash]={r,a};
}
void ins(int &p,int l,int r,int s,int t){
if(s<=l&&r<=t){
p=-1;
return;
}
if(!p) p=++cntnod;
int mid=(l+r)/2;
siz[p]+=std::min(r,t)-std::max(l,s);
if(s<mid) ins(trie[p][0],l,mid,s,t);
if(mid<t) ins(trie[p][1],mid,r,s,t);
}
int main(){
int i,l,r,o,n;
scanf("%d%d",&n,&K);
while(n--){
scanf("%d%d",&l,&r);
ins(rt,0,V,l,r+1);
}
auto [_,res]=solve(1,1,0,W);
printf("%d",res);
return 0;
}