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

这是这场比赛第二水的一题,但我并没有切。

题解:

正解其实很简单,很容易可以发现一个性质,讲一个序列排序,xor值最小的值肯定出现在相邻两个数之间,
证明:设a<b<c,那么我们只需要证明min(ab,bc)<a^c设到第t位开始第一次出现a,b,c在第t位上的值不同,有两种情况:
1.(0,0,1) 此时ab<ac
2. (0,1,1) 此时bc>ac
综上所述,xor值最小的值肯定出现在相邻两个数之间。
有了这个性质,我们就很容易可以想出正解:
我们设f[i]表示以i结尾的序列个数,那么我们有f[i]=sigma(f[j] (a[i]^a[j]>=x) )+1。
这个转移可以用trie来优化。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500000
#define K 63
#define ll long long
#define mo 998244353
using namespace std;
ll n,X,a[N],i,f[N],ans,num;
struct trie{
	ll sum,s[2];
}tr[N*K];
void insert(ll x,ll s,ll val,ll t){
	if (t>K){
		tr[x].sum+=val;
		return;
	}
	ll p=s>>(K-t);
	if (!tr[x].s[p&1]) tr[x].s[p&1]=++num;
	insert(tr[x].s[p&1],s,val,t+1);
	tr[x].sum=tr[tr[x].s[0]].sum+tr[tr[x].s[1]].sum;
}
ll get(ll x,ll s,ll t,ll X){
	if (!x) return 0;
	ll p=(s>>(K-t))&1,val=1ll<<(K-t),sum=0;
	if (val>=X) sum+=tr[tr[x].s[1-p]].sum,sum%=mo;
	if (val>X) sum+=get(tr[x].s[p],s,t+1,X),sum%=mo;
	if (val<X) sum+=get(tr[x].s[1-p],s,t+1,X-val),sum%=mo;
	return sum;
}
int main(){
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	scanf("%lld%lld",&n,&X);
	for (i=1;i<=n;i++) scanf("%lld",&a[i]);
	sort(a+1,a+n+1);
	num=1;
	for (i=1;i<=n;i++){
		f[i]=1+get(1,a[i],1,X);
		ans=(ans+f[i])%mo;
		insert(1,a[i],f[i],1);
	}
	printf("%lld\n",ans%mo);
	return 0;
}
posted @ 2020-10-27 20:18  Mohogany  阅读(86)  评论(0编辑  收藏  举报