专栏文章

题解:P10200 [湖北省选模拟 2024] 花神诞日 / sabzeruz

P10200题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mir4ns15
此快照首次捕获于
2025/12/04 15:41
3 个月前
此快照最后确认于
2025/12/04 15:41
3 个月前
查看原文
今年二月份的时候不会做,现在看来觉得自己当时好菜啊。

思路

首先是一个结论,a<b<c,ac>min{ab,bc}\forall a<b<c,a\oplus c>\min\{a\oplus b,b\oplus c\},其中 \oplus 是异或。换句话说,一个集合中异或和最小的两个数必然是从小到大排序后相邻的两个数。证明考虑丢到 trie 树上找到第一个不同位。
所以给 aa 排个序,问题变成给每个元素染成红蓝两种颜色之一,要求相邻红色的异或和 k1\ge k_1,相邻蓝色异或和 k2\ge k_2,必须两种颜色都有,求方案数。
这个题意长得很像什么?是不是很像 CSP-S2024 T3?
考虑一样地设状态。设 dp(i,j,0/1)dp(i,j,0/1) 表示当前已经决定了前 ii 个元素的颜色,第 ii 个元素的颜色是红/蓝,和 ii 颜色不同的最后一个元素是 jj
转移分两种,首先是 i+1i+1ii 颜色相同,此时 ji1j\le i-1
dp(i+1,j,0)=dp(i,j,0)[ai+1aik1]dp(i+1,j,0)=dp(i,j,0)[a_{i+1}\oplus a_i\ge k_1]
dp(i+1,j,1)=dp(i,j,1)[ai+1aik2]dp(i+1,j,1)=dp(i,j,1)[a_{i+1}\oplus a_i\ge k_2]
其中方括号是艾弗森括号。然后是 i+1i+1ii 颜色不同,也就是和 jj 颜色相同:
dp(i+1,i,0)=j=0i1dp(i,j,1)[ai+1ajk1]dp(i+1,i,0)=\sum\limits_{j=0}^{i-1}dp(i,j,1)[a_{i+1}\oplus a_j\ge k_1]
dp(i+1,i,1)=j=0i1dp(i,j,0)[ai+1ajk2]dp(i+1,i,1)=\sum\limits_{j=0}^{i-1}dp(i,j,0)[a_{i+1}\oplus a_j\ge k_2]
初值为 a0=,dp(1,0,0)=dp(1,0,1)=1a_0=\infty,dp(1,0,0)=dp(1,0,1)=1。答案为 i=1n1(dp(n,i,0)+dp(n,i,1))\sum\limits_{i=1}^{n-1}(dp(n,i,0)+dp(n,i,1))。注意此处 ii 是从 11 开始,因为两种颜色都要有。
考虑优化。我们对 ii 扫描线,维护数列 dp(i,,0),dp(i,,1)dp(i,*,0),dp(i,*,1) 的变化。
先考虑后两个转移。需要快速维护满足 ai+1ajkta_{i+1}\oplus a_j\ge k_tdp(i,j,tt)dp(i,j,t\oplus t) 的和。异或问题丢上 01-trie。我们在 aja_j 对应的叶子处维护 dp(i,j,)dp(i,j,*),每个节点维护子树和。上面这个问题很容易在 trie 上做,如果 ktk_t 的这一位是 11 就递归到这一位异或 ai+1a_{i+1}11 的子树,否则返回异或为 11 的子树和加上递归到异或后为 00 的子树的结果。
然后再是前两个。既然上 trie 树了那也好处理,如果 ai+1aikta_{i+1}\oplus a_i\ge k_t 就不修改 dp(i,,t)dp(i,*,t),否则打上 tag 赋值为 00,访问的时候像线段树一样下传 tag 即可。
最后统计答案不能去 a0a_0 的子树,原因同上。
时间复杂度 O(nlogV)O(n\log V)

代码

CPP
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
using namespace std;
const int MAXN=2e5+1,MOD=1e9+7;
int n;
ll a[MAXN],k[2];
int rt=1,cnt=1,nxt[MAXN*64][2],s[2][MAXN*64],tag[MAXN*64];
inline void psd(int now){
	if(tag[now]&1){
		if(nxt[now][0]) s[0][nxt[now][0]]=0,tag[nxt[now][0]]|=1;
		if(nxt[now][1]) s[0][nxt[now][1]]=0,tag[nxt[now][1]]|=1;
	}
	if(tag[now]&2){
		if(nxt[now][0]) s[1][nxt[now][0]]=0,tag[nxt[now][0]]|=2;
		if(nxt[now][1]) s[1][nxt[now][1]]=0,tag[nxt[now][1]]|=2;
	}
	tag[now]=0;
	return;
}
inline void upd(int now){
	(s[0][now]=s[0][nxt[now][0]]+s[0][nxt[now][1]])>=MOD&&(s[0][now]-=MOD);
	(s[1][now]=s[1][nxt[now][0]]+s[1][nxt[now][1]])>=MOD&&(s[1][now]-=MOD);
	return;
}
void mdf(int&now,int dep,ll v,int val,int id){
	if(!now) now=++cnt;
	if(dep==-1) return s[id][now]=val,void();
	psd(now);
	mdf(nxt[now][v>>dep&1],dep-1,v,val,id);
	upd(now);
	return;
}
int qry(int now,int dep,ll v,bool id){//s[id^1] of v^a[i]>=k[id]
	if(!now) return 0;
	if(dep==-1) return s[id^1][now];
	psd(now);
	if(k[id]>>dep&1) return qry(nxt[now][(v>>dep&1)^1],dep-1,v,id);
	else{
		int ans=s[id^1][nxt[now][(v>>dep&1)^1]]+qry(nxt[now][v>>dep&1],dep-1,v,id);
		ans>=MOD&&(ans-=MOD);
		return ans;
	}
	return 0;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>k[0]>>k[1];
	F(i,1,n) cin>>a[i];
	sort(a+1,a+n+1);
	a[0]=1ll<<60;
	mdf(rt,60,a[0],1,0),mdf(rt,60,a[0],1,1);
	F(i,1,n-1){
		int dp0,dp1;
		dp0=qry(rt,60,a[i+1],0),dp1=qry(rt,60,a[i+1],1);
		if((a[i+1]^a[i])<k[0]) tag[rt]|=1,s[0][rt]=0;
		if((a[i+1]^a[i])<k[1]) tag[rt]|=2,s[1][rt]=0;
		mdf(rt,60,a[i],dp0,0),mdf(rt,60,a[i],dp1,1);
	}
	cout<<((s[0][rt]+s[1][rt]-s[0][nxt[rt][1]]-s[1][nxt[rt][1]])%MOD+MOD)%MOD;
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...