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

题目

https://gmoj.net/senior/#main/show/6829

题解

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

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

  • 若它们的最高m位相等,这时 a ⨁ b , b ⨁ c , a ⨁ c a\bigoplus b,b\bigoplus c,a\bigoplus c ab,bc,ac的前m位都为0,相等,因此考虑第m+1位。
  • x , y , z x,y,z x,y,z表示 a , b , c a,b,c a,b,c的第m+1位,现在分类讨论:
    • x = y < z x=y<z x=y<z,则 min ⁡ ( x ⨁ y , y ⨁ z ) = x ⨁ y = 0 < 1 = x ⨁ z \min(x\bigoplus y,y\bigoplus z)=x\bigoplus y=0<1=x\bigoplus 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\bigoplus y,y\bigoplus z)=y\bigoplus z=0<1=x\bigoplus 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,都有\min(a\bigoplus b,b\bigoplus c)\le a\bigoplus c a,b,cN,abc,min(ab,bc)ac

有了这个结论之后就好做了。
先将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\bigoplus a_j\ge x}f_j fi=1+1j<i,aiajxfj
这样子是 O ( n 2 ) O(n^2) O(n2)的,考虑怎么优化。发现可以在这个异或上面做文章。

对排序后的序列建一棵trie,用它来优化DP,这里不妨将x减去1,这样只用处理 a i ⨁ a j > x a_i\bigoplus 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 ′ , a i ′ x',a'_i x,ai表示 x x x a i a_i ai当前为上的数,0或1):

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

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

CODE

#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;
}

猜你喜欢

转载自blog.csdn.net/huangzihaoal/article/details/109345002
今日推荐