xor(vector+字典树)

news/2024/11/28 18:41:33/

题目大意

给出一个长度为nnn的序列AAA,再给出一个整数xxx,如果一个子序列满足以下的条件,则它是一个符合条件的子序列:

  • 序列中的任意两个数的异或结果都大于等于xxx

求符合条件的子序列的个数,模998244353998244353998244353

两个子序列不同,当且仅当它们取自于原序列中的位置中有至少一个位置不同。

数据范围

1≤n≤3×105,1≤Ai≤260,1≤x≤2601\leq n\leq 3\times 10^5,1\leq A_i\leq 2^{60},1\leq x\leq 2^{60}1n3×105,1Ai260,1x260


题解

xxx的最高位为第ddd位。如果有两个数,它们在第ddd位以上的部分不同,那么显然它们异或之后一定有一位比第ddd位高且为111。也就是说,如果有两个数,它们在第ddd位以上的部分不同,那么它们不会有冲突。

我们把序列AAA中具有相同的第ddd位以上的部分的数放在一起,那么整个序列就被分成若干个同前缀序列。我在其中一个同前缀序列中选符合条件的数,并不影响我在另一个同前缀序列中选的数。所以我们可以先把每个同前缀序列中符合条件的子序列个数求出,然后求积,即可得出答案。

那么怎么求一个同前缀序列中符合条件的子序列个数呢?

因为分在一个同前缀序列中的数的第ddd位以上的部分相同,所以任意两个数的异或和的最高位一定不高于第ddd位。假如当前的子序列有两个数,那么这两个数的第ddd位一定满足一个是000。一个是111,否则异或和的第ddd位为000,也就小于xxx。如果我们要再加一个数,那么无论这个数的第ddd位是000还是111,它与先前的数异或之后一定会有一种情况使得第ddd位为000。所以,可以证明,在一个同前缀序列中符合条件的子序列的数的个数最多只有两个。

数的个数为000111的子序列的个数很好求,分别为111和该同前缀序列的大小。若子序列的数的个数为222,那么对于每一个数,我们需要找到另一个数,使得这两个数的异或和大于等于xxx

我们可以用字典树来维护。假设当前枚举的数为vvv,那么在字典树中:

  • 如果xxx在当前这一位为111,则这两个数必须不同,我们就往vvv的这一位的另一个数的一边查找
  • 如果xxx在当前这一位为000,则这两个数可以不同,我们就把vvv的这一位的另一个数的一边的数的和加到当前的答案上,在往vvv的这一位的数的一边查找

求出每一个同前缀序列的答案求出后,将每个同前缀序列的答案求积,即为最终的答案。

不要忘了最后减去序列为空的情况。

时间复杂度为O(60×n)O(60\times n)O(60×n)

code

#include<bits/stdc++.h>
using namespace std;
int n,v1=0,tot,siz[15000005],ch[15000005][2];
long long x,tx,s=1,ans=1,now,mi[65],a[300005],v[300005];
long long mod=998244353;
vector<long long>w[300005];
void pt(long long v){int q=1,vq;for(int i=60;i>=0;i--){vq=(v>>i)&1;if(!ch[q][vq]) ch[q][vq]=++tot;q=ch[q][vq];++siz[q];}
}
void find(long long v){int q=1,vq;for(int i=60;i>=0;i--){vq=(v>>i)&1;if((x>>i)&1){if(!ch[q][vq^1]) return;q=ch[q][vq^1];}else{if(ch[q][vq^1]) now=(now+siz[ch[q][vq^1]])%mod;if(!ch[q][vq]) return;q=ch[q][vq];}}now=(now+siz[q])%mod;
}
void cl(int x){if(ch[x][0]) cl(ch[x][0]);if(ch[x][1]) cl(ch[x][1]);ch[x][0]=ch[x][1]=0;siz[x]=0;
}
int main()
{scanf("%d%lld",&n,&x);mi[0]=1;for(int i=1;i<=60;i++) mi[i]=mi[i-1]*2;tx=x;while(x){x>>=1;s<<=1;}s=mi[60]-s;x=tx;for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}sort(a+1,a+n+1);for(int i=1;i<=n;i++){if(v1==0||(a[i]&s)!=v[v1]){v[++v1]=(a[i]&s);}w[v1].push_back((a[i]|s)^s);}for(int i=1;i<=v1;i++){int l=w[i].size();now=l+1;tot=1;for(int j=0;j<l;j++){find(w[i][j]);pt(w[i][j]);}ans=ans*now%mod;cl(1);}ans=(ans+mod-1)%mod;printf("%lld",ans);return 0;
}

http://www.ppmy.cn/news/40195.html

相关文章

针对近日ChatGPT账号大批量封禁的理性分析

文 / 高扬 这两天不太平。 3月31号&#xff0c;不少技术圈的朋友和我闲聊说&#xff0c;ChatGPT账号不能注册了。 我不以为然&#xff0c;自己有一个号足够了&#xff0c;并不关注账号注册的事情。 后面又有不少朋友和我说ChatGPT账号全部不能注册了&#xff0c;因为老美要封锁…

Winform控件开发(30)——MonthCalendar(史上最全)

前言 MonthCalendar控件用于显示日期,也就是某年某月某日 一、属性 1、Name 用户获取控件对象 2、AllowDrop 指示是否允许用户拖动数据到控件上 3、Anchor 设置控件相对于父控件锚定的位置 4、AnnuallyBoldedDates 设置每一年的该日期是否加粗显示,该属性的值是一个…

【kubernetes-工具篇】K9S详解-宝藏k8s界面工具

K9S简介 K9s是一个命令行界面&#xff08;CLI&#xff09;工具&#xff0c;用于管理Kubernetes集群。它是一个流行的开源工具&#xff0c;可以帮助Kubernetes管理员和开发人员轻松管理他们的Kubernetes集群。在本文中&#xff0c;我们将简单介绍K9s的概念、功能和如何使用它。…

Oracle的学习心得和知识总结(十八)|Oracle数据库性能压测工具swingbench的安装和使用及AWR ASH ADDM报告生成

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Guid…

【Kubernetes 企业项目实战】11、掌握 Kubernetes Kustomize 技术从入门到企业实战(下)

目录 一、Kustomize 进阶 1.1 使用覆盖定制资源 1.1.1 kustomization.yaml 1.1.2 deploy-patch.yaml 1.1.3 ing-patch.yaml 1.1.4 op 操作类型介绍 二、生成测试环境定制资源 三、使用 Kustomize 来应用、查看和删除对象 3.1 创建应用 3.2 查看应用 3.3 删除应用 四…

在PHP中输出JS语句以及乱码问题的解决方案

在PHP中输出JS语句以及乱码问题的解决方案 怎样在php中输出js语句&#xff1f; 示例 $classState""; if($state0){ $classState"已下课"; } else{ $classState"正在上课"; } echo ""; ?> 这样在页面的其他地方,就可以直接引用php中…

学习系统编程No.14【动静态库】

引言&#xff1a; 北京时间&#xff1a;2023/4/3/7:06&#xff0c;刚刚晨跑回来&#xff0c;为了摆脱困意&#xff0c;刷了一下视屏&#xff0c;哈哈哈&#xff01;我发现我每次刷视屏都是被迫的&#xff0c;都是看到某个感兴趣的标题&#xff0c;然后点进去一看&#xff0c;就…