题面看这里
题目大意
给出 \(n\) 份答卷,每份答卷上有 \(m\) 个问题,每个问题的答案是 \(A~or~B\)。
定义一个问题子集是可辩别的,当且仅当该集合非空,且有大于等于 \(k\) 对答卷在该问题子集上的答案是不完全相同的。
问你可辩别的问题子集数量。
\((1 \le n \le 2 \times 10^5,~1 \le m \le 20,~1 \leq k \leq \frac{n(n-1)}{2})\)
题目分析
数据范围 \(1\leq m\leq20\),一看就知道是个位运算的题,很自然地想到把答卷转化为01串,方便用各种位运算集合操作枚举状态和贡献。
考虑从 \(n\) 份答卷种取出任意的两份答卷作为一对,有 \(\dfrac {n*(n-1)}2\) 对答卷,每对答卷的状态都可以用一个十进制数 \(S\in[0,2^m-1]\) 来表示(\(S\) 也可以看作是一个集合),\(S\) 在二进制下第 \(i\) 位:为 0 代表这两份答卷的第 \(i\) 题答案相同;为 1 则代表答案不同。
对于每对答卷 \(S\),它能对哪些问题子集造成贡献?我们用一个十进制数 \(T\in[1,2^m-1]\) 来表示一个问题子集,\(T\) 在二进制下第 \(i\) 位:为 0 代表第 \(i\) 个问题不在 \(T\) 这个问题子集中;为 1 则代表在 \(T\) 中。显然如果有 \(S\cap T\neq\varnothing\),则 \(S\) 能对 \(T\) 造成一点贡献。举个栗子:
\(m=3\),则 \(T\in\{001,010,011,100,101,110,111\}\),如果有一个 \(S=2=(010)_2\),则代表有一对答卷第 1 题(下标从 0 开始)的答案不同,那么当取 \(T\in\{010,011,110,111\}\) 时,\(S\) 能够算作一对“在该问题子集上的答案不完全相同的答卷”,即造成一点贡献。
至此,一个很暴力的思路就出来了,枚举每个 \(T\in[1,2^m-1]\),同时暴力枚举每对答卷的状态 \(S\),判断是否存在 \(k\) 个以上的 \(S\) 交 \(T\) 不为空,如果存在,则 ans++,代码如下:
for(int t=1;t<1< int cnt=0; for(int i=1;i<=n;i++){ //双重循环枚举S for(int j=i+1;j<=n;j++){ int s=a[i]^a[j]; if(s&t) cnt++; //两个数与起来 相当于求交集 } } if(cnt>=k) ans++; } 时间复杂度 \(O(n^2*2^m)\),必炸。 考虑利用位运算优化,显然双重循环枚举 \(S\) 的时间复杂度过高,必须优化,怎么做呢? 显然一个 \(S\) 可能会由多对答卷组成,因此我们定义一个 \(F(S)\) 数组: \[F(S)=\sum_{i=1}^n\sum_{j=i+1}^n~\Big[a[i]\oplus a[j]=S\Big] \] \(F(S)=x\) 表示有 \(x\) 对答卷的状态为 \(S\)。 用一个十进制数 \(i\in[0,2^m-1]\) 来表示单独一张答卷的状态,\(i\) 二进制下第 \(j\) 位:为 0 代表这张答卷的第 \(j\) 题答案为 \(A\);为 1 则代表答案为 \(B\)。定义一个桶数组 num[i],表示状态为 \(i\) 的答卷的数量,那么可以得到: \[F(S)=\dfrac 12\sum_{i\oplus j=S}~num[i]*num[j] \] 这个形式我们就很熟悉了,裸的 FWT,我们可以在 \(O(m*2^m)\) 时间内求出 \(F(S)\) 数组,注意到 \(i=j\) 是非法情况,这样的情况总共会出现 \(n\) 次,此时 \(i\oplus j=0\),因此还应将 FWT 后求得的 \(F(0)\) 减去 \(n\)。 再定义一个 \(G(T)\) 数组: \[G(T)=\sum_{T\cap S\neq\varnothing}F(S) \] \(G(T)=x\) 代表当问题子集为 \(T\) 时,共有 \(x\) 对答卷能造成贡献,则 \(ans=\sum~[G(T)\geq k]\)。 发现 \(G(T)\) 并不好求,因为 \(\sum\limits_{T\cap S\neq\varnothing}F[S]\) 没啥好方法能够直接求出,只能暴力枚举。 于是我们做个容斥,定义 \(G(T)=x\) 代表当问题子集为 \(T\) 时,有 \(x\) 对答卷不能造成贡献,则有: \[G(T)=\sum_{T\cap S=\varnothing}F(S)=\sum_{S\subseteq(U-T)}F(S) \] 这里 \(U\) 是全集,用十进制数表示就是 \(2^m-1\),即 \(G(T)\) 计算的是 \(F(T 的补集的子集)\) 所造成的贡献之和,这也是老套路了,可以用 SOSdp 在 \(O(m*2^m)\) 时间内求出 \(G\) 数组,具体写法看下面的代码实现。 于是对于每个问题子集 \(T\),有 \(G(T)\) 个不能造成贡献的,则能造成贡献的有 \(\dfrac {n*(n-1)}{2}-G(T)\) 个,因此 \(ans=\sum~[\dfrac {n*(n-1)}{2}-G(T)\geq k]\)。 在 \(O(m*2^m)\) 的时间里预处理出 \(F\) 数组和 \(G\) 数组,最后再 \(2^m\) 次枚举计算答案,总时间复杂度 \(O(m*2^m)\)。 代码实现 #include using namespace std; const int maxn=3e6+6; long long n,m,k,limit,ans; long long f[maxn],g[maxn]; void fwt(long long *a,int type,int limit){ for(int i=1;i for(int j=0,step=i<<1;j for(int k=0;k
long long x=a[j+k],y=a[j+k+i]; a[j+k]=x+y,a[j+k+i]=x-y; if(type==-1) a[j+k]/=2,a[j+k+i]/=2; } } } } int main(){ ios::sync_with_stdio(false); cin >> n >> m >> k; limit=1< for(int i=1;i<=n;i++){ long long d=0,p=1; char now; for(int j=0;j cin >> now; now-='A'; d+=p*now; //把字符串转化为01串 相当于一个二进制数 再转化为一个十进制数 即分析里的答卷状态i } f[d]++; //这里的f还只是分析的桶数组num 等执行完fwt操作后才变成了f } fwt(f,1,limit); for(int i=0;i f[i]*=f[i]; } fwt(f,-1,limit); f[0]-=n; //去掉 i=j 的情况 limit--; for(int i=0;i<=limit;i++){ //初始化g数组 显然f[i的补集]会对g[i]造成贡献 g[i]=f[i^limit]/2; //与全1的数进行异或 相当于求补集 即i^limit是i的补集 } for(int i=0;i for(int j=0;j<=limit;j++){ if(1<
int k=1<
g[k]+=g[j]; //g[j]会对g[k]造成贡献 } } } for(int i=1;i<=limit;i++){ if(n*(n-1)-2*g[i]>=k*2) ans++; } cout << ans; } FWT 纯纯的套路,套板子就行,没什么好说的。其实是不会说 这里稍微说下 SOSdp: for(int i=0;i<=limit;i++){ //初始化g数组 显然f[i的补集]会对g[i]造成贡献 g[i]=f[i^limit]/2; //与全1的数进行异或 相当于求补集 即i^limit是i的补集 } for(int i=0;i for(int j=0;j<=limit;j++){ if(1<
int k=1<
g[k]+=g[j]; //g[j]会对g[k]造成贡献 } } } 这里我们要求的是补集的子集的贡献之和,而初始化时,我们让 g[i]=f[i^limit]/2,则问题转化为了求 \(G(T)\) 的超集的贡献之和,这一步可能有点抽象,举个栗子帮忙理解一下: \(m=3,~T=2=(010)_2\),则 \(G(T)=F(101)+F(100)+F(001)+F(000)=G(010)+G(011)+G(110)+G(111)\)。 然后就是 SOSdp 求超集之和了。这里的 \(j\) 枚举了每个问题子集,遍历 \(i\) 从 \(0\sim m-1\) 从而枚举每个 \(j\) 的每一位。 如果 \(j\) 的第 \(i\) 位为 1,则 g[k]+=g[j]。什么意思呢?令 \(k=1<
这样计算贡献显然是不重不漏的,因为我们是从小到大枚举的,且每个集合都只对跟它只有一位不同的集合造成贡献。
- 世界杯进球提醒网:球迷的观赛利器,不错过每一个精彩瞬间在激情四溢的世界杯期间,每一个进球都可能成为历史的见证。为了帮助广大球迷不错过任何一个精彩瞬间,世界杯进球提醒网应运而生,成为...
- 2024中乙联赛评选结果揭晓:最佳球员、最佳青年总汇!在2024赛季中乙联赛中,各项评选结果如期而至,单轮最佳球员、月度最佳球员、最佳青年球员及最佳教练均已揭晓。这场激烈的角逐不仅彰显了...
- 北京市数独比赛火热报名中,智力与激情的碰撞不容错过!随着2023年世界杯足球赛的余热未消,北京的体育与智力竞技氛围依旧浓厚。近日,北京市体育局与数独协会联合宣布,一年一度的北京市数独比...
- 回顾2006年德国世界杯决赛:意大利与法国的巅峰对决2006年的夏天,整个世界都沉浸在足球的狂欢之中,德国作为东道主成功举办了第18届国际足联世界杯。这届世界杯不仅因为其精彩纷呈的比赛而...
- [豪门盛宴]20180712 创造历史首页 直播/赛程 时间赛程 球队赛程 小组赛赛程 淘汰赛赛程 VIP 视频 完整回放 进球大全 360度VR观赛 豪门盛宴 我爱世界杯 星耀金杯 世界杯最前...
- 中国台北队归化中锋引发争议 亚手联启动球员资格审查程序中国台北队归化中锋引发争议 亚手联启动球员资格审查程序2025-08-28在近日的亚洲篮球赛事中,中国台北队归化中锋的事件引发了广泛的争议。...
- 中国球员NBA得分排行榜,谁最可惜1 / 5 中国球员NBA得分排行榜,谁最可惜?① 姚明 火箭队 9247分② 易建联 湖人队 2148分③ 王治郅 小牛队 604分④ 巴特尔 马刺队156分⑤ 周琦 火箭...
- 足彩新规解读:为何只能串当天比赛?揭秘背后的竞彩逻辑与玩家策略 当日的激情,当日的博弈 最近不少足彩玩家发现,各大竞彩平台悄然调整了串关规则——只能选择当天进行的比赛进行组合投注。这一变化让...
- 西甲流氓球员再惹争议:世界杯赛场上谁在威胁公平竞争 随着2023年世界杯的脚步渐近,全球球迷的目光再次聚焦在绿茵场上。然而,一场关于“西甲流氓球员”的争议却在近期引发了广泛讨论。这些...
- 最美地铁,好想去坐坐。坐地铁是太普通不过的事情了,同坐公交车、火车一样平常。但是当你坐上这样的地铁,是不是生活多了一种享受了呢? .................................