P10200 [湖北省选模拟 2024] 花神诞日 题解
首先注意到一个集合中两两异或和的最小值就是,排序后相邻两个数异或和的最小值。证明可以考虑放到 01-Trie 上,从高往低位建树,求一个数与之异或的最小值,就是使高位相同位数尽可能多,则就是 01-Trie 上的前一个叶子或后一个叶子。
由此,我们可以设一个 \(O(n^2)\) 的 DP,把 \(a\) 排序后,设 \(f_{i,j}\) 表示考虑完了前 \(\max(i,j)\) 个数后,第一道菜考虑最后选了 \(i\),第二道菜最后选了 \(j\) 的方案数。转移就是考虑下一位与当前位是否属于同一道菜。这样我们能写出以下代码并获得 32 分。
const int mod=1e9+7,N=5e3+5;
ll a[N],k1,k2;
int n,f[N][N];
void add(int &x,int y) {x+=y;if(x>=mod) x-=mod;
}
signed main(){read(n,k1,k2);fo(i,1,n) read(a[i]);sort(a+1,a+1+n);f[1][0]=f[0][1]=1;fo(i,2,n) {fu(j,0,i-1) {if((a[i]^a[i-1])>=k1) add(f[i][j],f[i-1][j]);if((a[i]^a[i-1])>=k2) add(f[j][i],f[j][i-1]);if(!j||(a[i]^a[j])>=k1) add(f[i][i-1],f[j][i-1]);if(!j||(a[i]^a[j])>=k2) add(f[i-1][i],f[i-1][j]);}}int ans=0;fu(i,1,n) add(ans,f[n][i]),add(ans,f[i][n]);write(ans);return 0;
}
这样的状态不太简洁,事实上我们可以设 \(f_{i,j,0/1}\) 表示考虑完前 \(i\) 个位置,上一个与 \(i\) 颜色不同的位置是 \(j(j<i)\),且第 \(i\) 位属于第一道还是第二道菜的方案数。这与上面是等价的。则有如下转移(从 \(1\) 转移是对称的):
其中初始状态为 \(f_{1,0,0}=f_{1,0,1}=1\)。
考虑优化 DP。发现第一条转移与 \(j\) 无关,我们可以直接继承。而第二条转移仅仅是把满足条件的 \(j\) 求和,可以把 DP 数组中的每个数放在 01-Trie 上,动态开点存在 \(a_j\) 代表的叶子中。由于条件是 \(a_{i+1}\oplus a_j\ge k_2\) 所以可以 \(O(\log V)\) 求和。时间复杂度 \(O(n\log V)\)。
AC 代码,注意最后要减掉其中一道菜不选的情况:
const int mod=1e9+7,N=2e5+5;
const int L=1.5e7;
int n;
ll a[N],k1,k2;
void add(int &x,int y) {x+=y;if(x>=mod) x-=mod;
}
struct trie {int tot,tr[L][2],s[L];void insert(int &x,ll w,int v,int val) {if(!x) x=++tot,tr[x][0]=tr[x][1]=s[x]=0;if(v<0) {add(s[x],val); return;}insert(tr[x][w>>v&1],w,v-1,val);s[x]=(s[tr[x][0]]+s[tr[x][1]])%mod;}int query(int x,ll w1,ll w2,int v) {if(!x) return 0;if(v<0) return s[x];int o1=w1>>v&1,o2=w2>>v&1;if(o2) return query(tr[x][o1^1],w1,w2,v-1);else return (query(tr[x][o1],w1,w2,v-1)+s[tr[x][o1^1]])%mod;}int q0(int x,int v) {if(!x) return 0;if(v<0) return s[x];return q0(tr[x][0],v-1);}
}t1,t2;
int rt1,rt2;
signed main(){read(n,k1,k2);fo(i,1,n) read(a[i]);sort(a+1,a+1+n);t1.insert(rt1,0,59,1),t2.insert(rt2,0,59,1);fo(i,2,n) {int s1=t1.query(rt1,a[i],k2,59);int s2=t2.query(rt2,a[i],k1,59);if(a[i]<k2) add(s1,t1.q0(rt1,59));if(a[i]<k1) add(s2,t2.q0(rt2,59));if((a[i]^a[i-1])<k1) rt1=0,t1.tot=0;if((a[i]^a[i-1])<k2) rt2=0,t2.tot=0;t1.insert(rt1,a[i-1],59,s2),t2.insert(rt2,a[i-1],59,s1);}write(((ll)mod+t1.s[rt1]+t2.s[rt2]-t1.q0(rt1,59)-t2.q0(rt2,59))%mod);return 0;
}