专栏文章
题解:CF2056F2 Xor of Median (Hard Version)
CF2056F2题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miniyrw7
- 此快照首次捕获于
- 2025/12/02 03:11 3 个月前
- 此快照最后确认于
- 2025/12/02 03:11 3 个月前
引理
若 的二进制上 的位置集合是 的子集,则记作 。所有变量都是整数。
引理 1: 为奇数,当且仅当 。
引理 2: 为奇数,当且仅当 。( 为按位与)。
引理 3: 为奇数,当前仅当 是 的二进制划分。即 ,
引理 4:
此处只证明引理 3。首先 要是奇数,所以 ,然后 也要是奇数,所以 去掉 的部分要包含 。以此类推, 就是 的二进制划分。
F1 题解
首先,可以枚举 数组,考虑对答案的贡献。由引理 3,若一种 想对答案产生贡献,必须是 的二进制划分。此时最大的 一定超过总和的一半,所以中位数就是最大值。
记 中 的位置数为 ,枚举 数组不为零的位置数 、原数组 的最大值 ,答案为:
这里简单解释一下,首先 个二进制位要分成 份后排序,这里的方案数就是第二类斯特林数,然后原数组要从 中选 种数( 已经是最大值了)。
这样 F1 就做完了,使用引理 1、引理 2 计算,时间复杂度 。
CPP#include<bits/stdc++.h>
using namespace std;
int t,k,n,m,ans;
char n2[200005];
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d%d%s",&k,&m,n2);
n=0;
for(int i=0; i<k; i++) n+=(n2[i]=='1');
ans=0;
for(int y=1; y<=min(n,m); y++)
for(int x=0; x<m; x++)
if(((y-1)|x)==x && ((n-y)&((y-1)/2))==0)
ans^=x;
printf("%d\n",ans);
}
return 0;
}
F2 题解
记:
答案就是 。
我们分成两个部分进行优化,第一部分是计算 ,第二部分是快速求异或和。
第一部分的可以使用 sosdp。
注意到 的高位均没有用。形式化地,令 是最小的满足 的数,。因此只需要处理 部分的 就行了,这一部分时间复杂度 。
第二部分,由引理 4 可以简单计算。
CPP#include<bits/stdc++.h>
using namespace std;
int t,k,n,m,N,ans;
char n2[200005];
int f[300005];
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d%d%s",&k,&m,n2);
n=0;
for(int i=0; i<k; i++) n+=(n2[i]=='1');
ans=0;
int N=0;
while((1<<N)<=min(n,m)) N++;
for(int j=0; j<(1<<N); j++) f[j]=(j<n && j<m && (((n-j-1)&(j/2))==0));
for(int i=0; i<N; i++)
for(int j=0; j<(1<<N); j++)
if(j&(1<<i))
f[j]^=f[j^(1<<i)];
// 分块计算
int t=0, cnt=0;
for(int x=0; x<(1<<N); x++)
if(f[x])
t^=x, cnt++;
int x=0, bk=m>>N;
if(bk&1) ans^=t;
if(cnt&1) {
// 0 xor 1 xor 2 xor ... xor bk-1
switch((bk-1)&3) {
case 0: {
ans^=((bk-1)>>2<<2)<<N;
break;
}
case 1: {
ans^=1<<N;
break;
}
case 2: {
ans^=(((bk-1)>>2<<2)+3)<<N;
break;
}
}
}
for(int x=((m>>N)<<N); x<m; x++) ans^=x*f[x%(1<<N)];
printf("%d\n",ans);
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...