gmoj 6829 / 7038. 异或 题解

题目

https://gmoj.net/senior/#main/show/6829
https://gmoj.net/senior/#main/show/7038
想不到,半年之后,居然遇到了重题!

题解

首先证明一个结论:选取后序列的异或最小值只可能为序列排序后相邻元素的异或值的最小值。
证明:

设当前有 a ≤ b ≤ c a\le b\le c abc ,现在只需证明 min ⁡ ( a ⊕ b , b ⊕ c ) ≤ a ⊕ c \min(a\oplus b,b\oplus c)\le a\oplus c min(ab,bc)ac 即可。

  • 若它们的最高 m m m 位相等,这时 a ⊕ b , b ⊕ c , a ⊕ c a\oplus b,b\oplus c,a\oplus c ab,bc,ac 的前 m m m 位都为 0 0 0 ,相等,因此考虑第 m + 1 m+1 m+1 位。
  • x , y , z x,y,z x,y,z 表示 a , b , c a,b,c a,b,c 的第 m + 1 m+1 m+1 位,现在分类讨论:
    • x = y < z x=y<z x=y<z ,则 min ⁡ ( x ⊕ y , y ⊕ z ) = x ⊕ y = 0 < 1 = x ⊕ z \min(x\oplus y,y\oplus z)=x\oplus y=0<1=x\oplus z min(xy,yz)=xy=0<1=xz
    • x < y = z x<y=z x<y=z ,则 min ⁡ ( x ⊕ y , y ⊕ z ) = y ⊕ z = 0 < 1 = x ⊕ z \min(x\oplus y,y\oplus z)=y\oplus z=0<1=x\oplus z min(xy,yz)=yz=0<1=xz
  • 因此 ∀ a , b , c ∈ N , a ≤ b ≤ c , 都有 min ⁡ ( a ⊕ b , b ⊕ c ) ≤ a ⊕ c \forall a,b,c\in N,a\le b\le c,\text{都有}\min(a\oplus b,b\oplus c)\le a\oplus c a,b,cN,abc,都有min(ab,bc)ac

有了这个结论之后就好做了。
先将 a a a 从小到大排序。
f i f_i fi 表示以 a i a_i ai 结尾的合法序列的个数。
那么有状态转移方程
f i = 1 + ∑ 1 ≤ j < i , a i ⊕ a j ≥ x f j f_i=1+\sum_{1\le j<i,a_i\oplus a_j\ge x}f_j fi=1+1j<i,aiajxfj
这样子是 O ( n 2 ) O(n^2) O(n2) 的,考虑怎么优化。发现可以在这个异或上面做文章。

对排序后的序列建一棵 trie ,用它来优化 D P DP DP ,这里不妨将 x x x减去 1 1 1 ,这样只用处理 a i ⊕ a j > x a_i\oplus a_j>x aiaj>x 的情况了。
每次插入新的 a i a_i ai 时,就先在 trie 上求出 f i f_i fi 的值,然后再将 a i a_i ai 加入 trie
f i f_i fi 时,可以从上往下走,满足当前节点到根的二进制前缀异或上 a i a_i ai 的相同长度前缀等于 x x x 的相同长度二进制前缀。
具体来说是这样的(用 x ′ , a i ′ x',a'_i x,ai 表示 x x x a i a_i ai 当前为上的数,值为 0 0 0 1 1 1 ):

  • x ′ = 0 x'=0 x=0 ,这时若能使 a i ′ ⊕ a j ′ = 1 a'_i\oplus a'_j=1 aiaj=1 ,就能将 f j f_j fj 累加入 f i f_i fi 中,因此加上以 a i ′ a'_i ai 的兄弟节点的子树内的 f f f 之和,接着走到 a i ′ a'_i ai 中;
  • x ′ = 1 x'=1 x=1 ,这时只能使 a i ′ ⊕ a j ′ = 1 a'_i\oplus a'_j=1 aiaj=1 ,因此走到 a i ′ a'_i ai 的兄弟节点上。

这样就做完了。
这道题目比较关键的地方是开头那个证明和用 trie 优化 D P DP DP

CODE

gmoj 6829. 【2020.10.25提高组模拟】异或

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define trans(x) (x?1:0)
#define P 998244353
#define M 20000005
#define N 300005
ll x,a[N];int f[N],sum[M],son[M][2],s=1,id;
inline void add(int &x,int y){x+=y;if(x>=P) x-=P;}
void qry(int k,ll a,ll num)
{
	if(k==0||!num) return;
	if(x&num) qry(son[k][trans(a&num)^1],a,num>>1);
	else
		add(f[id],sum[son[k][trans(a&num)^1]]),
		qry(son[k][trans(a&num)],a,num>>1);
}
void inc(int k,ll a,ll num)
{
	if(!num){add(sum[k],f[id]);return;}
	int to=trans(a&num);
	if(!son[k][to]) son[k][to]=++s;
	inc(son[k][to],a,num>>1);
	sum[k]=sum[son[k][0]]+sum[son[k][1]];
	if(sum[k]>=P) sum[k]-=P;
}
int main()
{
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	int n,s,ans=0;
	scanf("%d%lld",&n,&x);
	for(int i=1;i<=n;++i) scanf("%lld",a+i);
	if(!x)
	{
		ans=1;for(int i=1;i<=n;++i) ans=2LL*ans%P;
		--ans;if(ans<0) ans+=P;printf("%d\n",ans);
		return 0;
	}
	--x,sort(a+1,a+n+1);
	for(int i=1;i<=n;++i)
		f[++id]=1,qry(1,a[i],1LL<<59),
		inc(1,a[i],1LL<<59),add(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}

gmoj 7038. 2021.04.01【2021省赛模拟】异或 (inception)

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define fo(i,l,r) for(i=l;i<=r;++i)
#define M 20000005
#define N 300005
const int P=998244353;
ll x,a[N];
int f[N],son[M][2],sum[M],s=1,cnt;
inline void add(int &x,int y){x+=y;if(x>=P) x-=P;}
inline int addi(int x,int y){x+=y;return x>=P?x-P:x;}
void solve(int k,ll now,ll num)
{
	if(k==0||!num) return;
	int wh=now&num?1:0;
	if(x&num) solve(son[k][wh^1],now,num>>1);
	else
	{
		add(f[cnt],sum[son[k][wh^1]]);
		solve(son[k][wh],now,num>>1);
	}
}
void inc(int k,ll x,ll num)
{
	if(!num){add(sum[k],f[cnt]);return;}
	int to=x&num?1:0;
	if(!son[k][to]) son[k][to]=++s;
	inc(son[k][to],x,num>>1);
	sum[k]=addi(sum[son[k][0]],sum[son[k][1]]);
}
int main()
{
	freopen("inception.in","r",stdin);
	freopen("inception.out","w",stdout);
	register int i;
	int T,n,ans=0;
	scanf("%d%d%lld",&T,&n,&x);
	fo(i,1,n) scanf("%lld",a+i);
	if(!x)
	{
		ans=1;
		fo(i,1,n) ans=2LL*ans%P;
		printf("%d\n",addi(ans,P-1));
		return 0;
	}
	sort(a+1,a+n+1),--x;
	fo(i,1,n)
	{
		f[++cnt]=1,solve(1,a[i],1LL<<59);
		inc(1,a[i],1LL<<59),add(ans,f[i]);
	}
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值