P7156 [USACO20DEC] Cowmistry P 题解
给一个代码好写的做法。 首先假设建了完整的 Trie 树,那么三个不同的点 u,v,w 在 Trie 树上的虚树一定是 5 个点(包含叶子节点和 LCA)。我们钦定 v,w 的 LCA 是 x,x 和…
给一个代码好写的做法。
首先假设建了完整的 Trie 树,那么三个不同的点 在 Trie 树上的虚树一定是 5 个点(包含叶子节点和 LCA)。我们钦定 的 LCA 是 , 和 的 LCA 是 ,并且 深度大于 。
那么容易发现最大异或值一定出现在 或 中,钦定是 。考虑在 Trie 上 dfs 遍历 节点,类似于数位 dp,某些位置 为 我们就可以选 从而解除限制。让 ,同时找到同深度的 的节点,使得存在 满足前缀条件,这样就可以找到 在哪里解除限制,从而计算 的合法对数。
先找 解除限制的方案,设 返回 ,记为 。设 这一位为 ,记节点 的两个儿子是 。
如果当前位走 ,为了满足 的限制,递归计算 。分类讨论 :
- :此时 必须选相同的方向才能满足 (紧贴限制),无法通过选不同方向来解除限制,所以 。
- :
- 和 这一位选不一样(即 ):此时异或值为 1,等于 的当前位,限制仍然没有解除(紧贴限制),。
- 和 这一位选一样(即 ):此时异或值为 0,小于 的当前位 1, 在这一位解除了限制,后续可以随便选,所以 。
接下来计算答案。 第一种是 到当前位还没有解除限制的情况,并且钦定 在当前位解除限制,也就是 分叉在 的当前层。考虑对答案的贡献只能在 分叉之后,所以 时才能贡献。这里分类讨论 :
- :由于必须满足 这一位异或为 0,无法在 的位置让 分叉,所以不贡献答案,直接递归下去算。
- 有两种情况:
- 出分叉出 ,也就是 在这一位解除限制。此时 。只需要满足 即可(即 部分), 可以在 中任选,所以贡献 。
- 都在这一位相对于 解除了限制(即 ,而 都去往 )。此时 满足 且 。 在 子树中任意分叉均可,所以贡献 。
最后算一下 早就解除了限制,即在之前层级已经满足 的情况。此时若 的 LCA 在当前 节点处分叉(即 在 ,而 在 ),则贡献为 。
但是节点数量太多了,考虑把满二叉树都压缩一下,然后记忆化 函数即可。由于 一一对应, 为空不会遍历,而 都进入满二叉树就会被记忆化,所以复杂度 。
#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;
}谢谢你,读到这里。
本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗