DTOJ#5066. 异或

给定一个长度为 n n n 的非负整数列 a 1 , a 2 , … , a n a_1, a_2, \dots , a_n a1,a2,,an 和非负整数 x x x, 求有多少个非空子序列 1 ≤ b 1 < b 2 < ⋅ ⋅ ⋅ < b k ≤ n 1 \leq b_1 < b_2 < · · · < b_k \leq n 1b1<b2<<bkn,满足对任意的 ( i , j ) ( 1 ≤ i < j ≤ k ) (i, j)(1 \leq i < j \leq k) (i,j)(1i<jk) 都有 a b i ⊕ a b j ≥ x a_{b_i} \oplus a_{b_j} \ge x abiabjx。其中 ⊕ \oplus 表示按位异或。

由于这个数可能非常大,你只需要回答这个数除以 998244353 998244353 998244353 的余数即可。

第一行两个整数 n , x n, x n,x

第二行 n n n 个整数 a 1 , a 2 , . . . , a n a1, a2, . . . , an a1,a2,...,an

一行一个整数,表示满足条件的子序列的个数除以 998244353 998244353 998244353 的余数。

样例输入 1
3 0
0 1 2
样例输出 1
7
样例输入 2
3 2
0 1 2
样例输出 2
5
样例输入 3
3 3
0 1 2
样例输出 3
4
样例输入 4
7 4
11 5 5 8 3 1 3
样例输出 4
35
样例解释

第一组样例数据中,所有 2 3 − 1 2^3 − 1 231 个非空子序列都合法。

第二组样例数据中,除 [ 1 , 2 ] [1, 2] [1,2] [ 1 , 2 , 3 ] [1, 2, 3] [1,2,3] 外,所有非空子序列都合法。

第三组样例数据中,合法的子序列有 [ 1 ] , [ 2 ] , [ 3 ] [1], [2], [3] [1],[2],[3] [ 2 , 3 ] [2, 3] [2,3]

对于 100 % 100\% 100% 的测试点,满足 1 ≤ n ≤ 3 × 1 0 5 , 0 ≤ x , a i < 2 60 1 \leq n \leq 3 \times 10^5, 0 \leq x,a_i < 2^{60} 1n3×105,0x,ai<260

对于 10 % 10\% 10% 的测试点,满足 n ≤ 20 n \leq 20 n20

对于另外 20 % 20\% 20% 的测试点,满足 n ≤ 5000 n \leq 5000 n5000

对于另外 50 % 50\% 50% 的测试点,满足 n ≤ 1 0 5 n \leq 10^5 n105

jly csp2020 模拟1

设数组为 a [ ] a[] a[],按最高位升序排序,设 x x x最高位为 t o p x top_x topx
对于两个数 a [ i ] , a [ j ] a[i],a[j] a[i],a[j]
t o p i < t o p x , t o p j < t o p x top_i<top_x,top_j<top_x topi<topx,topj<topx,则 i , j i,j i,j不能同时选。
t o p i < t o p x , t o p j = = t o p x top_i<top_x,top_j==top_x topi<topx,topj==topx,此时用 t r i e trie trie树计算。
t o p i = = t o p x , t o p j = = t o p x top_i==top_x,top_j==top_x topi==topx,topj==topx,则 i , j i,j i,j不能同时选。
t o p i = = t o p j , t o p i > t o p x top_i==top_j,top_i>top_x topi==topj,topi>topx,此时去掉最高位,递归往下判断,最多 l o g log log层。
其他情况随意选,即乘法原理。
时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
但 远 远 达 不 到 \sout{但远远达不到}

#include<bits/stdc++.h>
#define N 300005
typedef long long ll;
using namespace std;
const int mod=998244353;
inline ll read(){
	ll x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	return x*f;
}
int tot=1,t[N*59][2],cnt[N*59];
ll fl[N*59],nfl,e[60];
inline void add(ll x){
	int now=1;
	for(int i=59;i+1;--i){
		int c=((x>>i)&1);
		if(fl[t[now][c]]!=nfl)t[now][c]=++tot,cnt[tot]=0,fl[tot]=nfl;
		cnt[now]++;now=t[now][c];
	}
	cnt[now]++;
}
inline int get(ll x,ll num){
	int ans=0,now=1;
	for(int i=59;i+1;--i){
		int c=((x>>i)&1),tc=((num>>i)&1);
		if(tc==0&&fl[t[now][c^1]]==nfl)ans=(ans+cnt[t[now][c^1]])%mod;
		now=t[now][tc^c];if(fl[now]!=nfl)return ans;
	}
	ans=(ans+cnt[now])%mod;
	return ans;
}
ll a[N];
inline int top(ll x){
	for(int i=59;i+1;--i){
		if((x>>i)&1)return i;
	}
	return -1;
}
inline int cal(int l,int mid,int r,ll num){
	for(int i=1;i<=tot;++i)t[i][0]=t[i][1]=0;
	tot=1;fl[tot]=++nfl;cnt[1]=0;
	for(int i=l;i<=mid;++i)add(a[i]);
	int ans=0;
	if(l<=mid){
	    for(int i=mid+1;i<=r;++i){
		    ans=(ans+get(a[i],num))%mod;
	    }
	}
	
	return ans+(r-mid);
}
int work(int l,int r,ll num){
	sort(a+l,a+r+1);
	int ans=1,low=0,mid=0,up=0,ntop=top(num),las=l-1,lto,c=1;
	for(int i=l;i<=r;++i){
		int to=top(a[i]);
		if(to<ntop)low++;
		if(to==ntop){
			if(mid==0)ans+=low;
			mid++;
		}
		if(to>ntop){
			if(up==0)ans=(ans+cal(l,l+low-1,l+low-1+mid,num))%mod,las=i,lto=to;
			++up;
			if(to!=lto){
				for(int j=las;j<i;++j){
					a[j]-=e[lto];
				}
				c=1ll*c*(work(las,i-1,num))%mod;
				las=i,lto=to;
			}
		}
	}
	if(mid==0)ans+=low;
	if(up==0)ans=(ans+cal(l,l+low-1,l+low-1+mid,num))%mod;
	if(las!=l-1){
		for(int j=las;j<=r;++j){
			a[j]-=e[lto];
		}
	    c=1ll*c*(work(las,r,num))%mod;
	}
	return 1ll*ans*c%mod;
}
inline int power(int x,int c){
	int now=1;
	while(c){
		if(c&1)now=1ll*now*x%mod;
		x=1ll*x*x%mod;c>>=1;
	}
	return now;
}
int main(){
	int n=read();ll x=read();
	e[0]=1;for(int i=1;i<=59;++i)e[i]=e[i-1]<<1;
	for(int i=1;i<=n;++i)a[i]=read();
	int ans;
	if(x==0)ans=power(2,n)-1;
	else ans=work(1,n,x)-1;
	printf("%d\n",(ans+mod)%mod);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值