P10200 花神诞日 题解

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_{i,j,0}\to f_{i+1,j,0}(a_{i+1}\oplus a_{i}\ge k_1) \]

\[f_{i,j,0}\to f_{i+1,i,1}(a_{i+1}\oplus a_j\ge k_2\lor j=0) \]

其中初始状态为 \(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;
}
posted @ 2025-01-11 15:23  dengchengyu  阅读(16)  评论(0)    收藏  举报