[CF1553H] XOR and Distance

\(\text{Problem}:\)XOR and Distance

\(\text{Solution}:\)

首先策略是,最高一位 \(a_{i}\oplus x\)\(a_{j}\oplus x\) 不同,使得这个最高位最低。然后尽量多的使后面位置 \(a_{i}\oplus x\)\(a_{j}\oplus x\) 不同且符号与最高位相反。

这里普通的做法是利用 \(01\text{ trie}\) 求解,实现较为复杂。CF 上某位神仙提供了一种复杂度与实现难度更低的做法

强制 \(f(x)\) 只能使用 \(a\) 中的一个子集,其中元素 \(p\) 满足二进制下,只有后 \(k\) 位可能与 \(x\) 不同。在此限制下,设 \((x,k)\) 表示此时可用元素的集合,\(f(x,k)\) 表示答案,\(ma(x,k)\)\(mi(x,k)\) 表示 \(x\oplus p\) 的最大值与最小值。考虑从 \(k\) 转移到 \(k+1\) 时,有以下几种转移:

  • \(f(x,k)\rightarrow f(x,k+1)\),由于 \((x,k)\) 对应集合一定是 \((x,k+1)\) 对应集合的子集,故直接转移。
  • 下面令 \(x,y\) 是一对只在 \(k+1\) 位上不同的数(没有其他的转移状态)。显然,\((a_{i}\oplus x)-(a_{j}\oplus x)=(a_{i}\oplus y)-(a_{j}\oplus y)\),故 \(f(y,k)\rightarrow f(x,k+1)\)
  • 否则,可以看作从 \(x\)\(y\) 的对应集合中分别取出一个元素来更新 \(f_{x,k+1}\)。不难发现,一定有 \(2^{k+1}\) 的贡献。此时,从 \((y,k)\) 对应集合中取一个与 \(y\) 异或最小的元素,即 \(mi(y,k)\),和 \(x\) 异或,再减去 \(ma(x,k)\),这显然是当前的最小答案。故有 \(2^{k+1}+mi(y,k)-ma(x,k)\rightarrow f(x,k+1)\)。同理,对于 \(y\) 的转移,有 \(2^{k+1}+mi(x,k)-ma(y,k)\rightarrow f(y,k+1)\)

然后考虑 \(ma\)\(mi\) 的更新。根据我们的定义,\((x,k+1)\) 对应集合就是将 \((x,k)\)\((y,k)\) 合并。这样就可以快速更新 \(ma(x,k+1)\)\(mi(x,k+1)\)

注意一对 \(x,y\) 只转移一次,不能重复转移。总时间复杂度 \(O(k2^{k})\)

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=1000010;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,K,f[N],ma[N],mi[N];
signed main()
{
	n=read(), K=read();
	memset(f,0x3f,sizeof(f));
	memset(ma,-0x3f,sizeof(ma));
	memset(mi,0x3f,sizeof(mi));
	for(ri int i=1;i<=n;i++)
	{
		int x=read();
		ma[x]=mi[x]=0;
	}
	for(ri int i=0;i<K;i++)
	{
		for(ri int x=0;x<(1<<K);x++)
		{
			if((x>>i)&1) continue;
			int y=x^(1<<i);
			f[x]=f[y]=min(f[x],f[y]);
			f[x]=min(f[x],(1<<i)+mi[y]-ma[x]);
			f[y]=min(f[y],(1<<i)+mi[x]-ma[y]);
			int px,py,qx,qy;
			px=ma[x], py=ma[y], qx=mi[x], qy=mi[y];
			ma[x]=max(ma[x],py+(1<<i));
			ma[y]=max(ma[y],px+(1<<i));
			mi[x]=min(mi[x],qy+(1<<i));
			mi[y]=min(mi[y],qx+(1<<i));
		}
	}
	for(ri int i=0;i<(1<<K);i++) printf("%d ",f[i]);
	puts("");
	return 0;
}
posted @ 2021-10-11 17:58  zkdxl  阅读(143)  评论(0编辑  收藏  举报