← 回到所有文章

P7156 [USACO20DEC] Cowmistry P 题解

给一个代码好写的做法。 首先假设建了完整的 Trie 树,那么三个不同的点 u,v,w 在 Trie 树上的虚树一定是 5 个点(包含叶子节点和 LCA)。我们钦定 v,w 的 LCA 是 x,x 和…

题解文章的简约插画封面

给一个代码好写的做法。

首先假设建了完整的 Trie 树,那么三个不同的点 u,v,wu,v,w 在 Trie 树上的虚树一定是 5 个点(包含叶子节点和 LCA)。我们钦定 v,wv,w 的 LCA 是 xx,xx 和 uu 的 LCA 是 yy,并且 xx 深度大于 yy。

那么容易发现最大异或值一定出现在 u⊕vu\oplus v 或 u⊕wu\oplus w 中,钦定是 u⊕wu\oplus w。考虑在 Trie 上 dfs 遍历 SS 节点,类似于数位 dp,某些位置 KK 为 11 我们就可以选 00 从而解除限制。让 u∈Su\in S,同时找到同深度的 TT 的节点,使得存在 w∈Tw\in T 满足前缀条件,这样就可以找到 w,vw,v 在哪里解除限制,从而计算 u,v,wu,v,w 的合法对数。

先找 ww 解除限制的方案,设 solve(S,T)\text{solve}(S,T) 返回 ∑u∈S,w∈T[u⊕w≤K]\sum_{u\in S,w\in T}[u\oplus w\leq K],记为 rr。设 KK 这一位为 kk,记节点 XX 的两个儿子是 X0,X1X_0, X_1。

如果当前位走 oo,为了满足 u⊕w≤Ku\oplus w \le K 的限制,递归计算 b=solve(So,To⊕k)b=\text{solve}(S_o, T_{o\oplus k})。分类讨论 kk:

  1. k=0k=0:此时 u,wu, w 必须选相同的方向才能满足 ≤K\le K(紧贴限制),无法通过选不同方向来解除限制,所以 r←br \leftarrow b。
  2. k=1k=1:
    1. SS 和 TT 这一位选不一样(即 u→o,w→o⊕1u \to o, w \to o \oplus 1):此时异或值为 1,等于 KK 的当前位,限制仍然没有解除(紧贴限制),r←br \leftarrow b。
    2. SS 和 TT 这一位选一样(即 u→o,w→ou \to o, w \to o):此时异或值为 0,小于 KK 的当前位 1,ww 在这一位解除了限制,后续可以随便选,所以 r←r+sizSo×sizTor \leftarrow r + \text{siz}_{S_o} \times \text{siz}_{T_{o}}。

接下来计算答案。 第一种是 ww 到当前位还没有解除限制的情况,并且钦定 vv 在当前位解除限制,也就是 xx 分叉在 TT 的当前层。考虑对答案的贡献只能在 yy 分叉之后,所以 S≠TS \neq T 时才能贡献。这里分类讨论 kk:

  1. k=0k=0:由于必须满足 u⊕wu \oplus w 这一位异或为 0,无法在 TT 的位置让 vv 分叉,所以不贡献答案,直接递归下去算。
  2. k=1k=1 有两种情况:
    1. TT 出分叉出 xx,也就是 vv 在这一位解除限制。此时 v∈To,w∈To⊕1v \in T_o, w \in T_{o\oplus 1}。只需要满足 u⊕w≤Ku\oplus w\leq K 即可(即 bb 部分),vv 可以在 ToT_o 中任选,所以贡献 b×sizTob \times \text{siz}_{T_o}。
    2. v,wv, w 都在这一位相对于 uu 解除了限制(即 u→ou \to o,而 v,wv, w 都去往 ToT_o)。此时 u,v,wu,v,w 满足 u⊕v<Ku \oplus v < K 且 u⊕w<Ku \oplus w < K。v,wv, w 在 ToT_o 子树中任意分叉均可,所以贡献 (sizTo2)×sizSo\binom{\text{siz}_{T_o}}{2} \times \text{siz}_{S_o}。

最后算一下 ww 早就解除了限制,即在之前层级已经满足 u⊕w<Ku \oplus w < K 的情况。此时若 u,v,wu,v,w 的 LCA yy 在当前 SS 节点处分叉(即 uu 在 SoS_o,而 v,wv,w 在 So⊕1S_{o\oplus 1}),则贡献为 ∑o(sizSo⊕12)×sizSo\sum_o \binom{\text{siz}_{S_{o\oplus 1}}}{2} \times \text{siz}_{S_o}。

但是节点数量太多了,考虑把满二叉树都压缩一下,然后记忆化 solve\text{solve} 函数即可。由于 S,TS,T 一一对应,SS 为空不会遍历,而 S,TS,T 都进入满二叉树就会被记忆化,所以复杂度 O(nlog⁡V)\mathcal{O}(n\log V)。

#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;
}

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭