题目描述
Each of Farmer John’s
N
N
N barns has selected a team of
C
C
C
cows to participate in field day. The breed of every cow is
either a Guernsey or a Holstein.
The difference between two teams is defined to be the number of positions
i
i
i
(
1
≤
i
≤
C
1 \leq i \leq C
1≤i≤C) at which the breeds of the cows in the
i
i
i th positions
differ. For every team
t
t
t from
1
…
N
1 \ldots N
1…N, please compute the maximum
difference between team
t
t
t and any other team.
翻译
有
N
N
N 个长度为
C
C
C 的由字符 G
或 c
组成的字符串。对于每个字符串,求出它与其他字符串不同位置数量的最大值。
题解
这里提供一个不同的思路。
首先把每个字符串压缩成一个数。数的范围显然是 [ 0 , 2 C ) [0,2^C) [0,2C),记第 i i i 个字符串对应的数为 a i a_i ai。
对于第 i i i 个询问,答案等于 max j ∈ [ 1 , N ] popcount ( a i ⊕ a j ) \max\limits_{j\in[1,N]}\operatorname{popcount}(a_i\oplus a_j) j∈[1,N]maxpopcount(ai⊕aj)
如果暴力做,时间复杂度是 O ( N 2 ) O(N^2) O(N2) 的,不能通过。
换一个思路,从大到小枚举答案 x x x,判断是否可行。若存在 y ∈ [ 0 , 2 C ) , j ∈ [ 1 , N ] y\in[0,2^C),j\in[1,N] y∈[0,2C),j∈[1,N],使得 popcount ( y ) = x \operatorname{popcount}(y)=x popcount(y)=x,且 y ⊕ a j = a i y\oplus a_j=a_i y⊕aj=ai,就是可行的。
观察关键条件: y ⊕ a j = a i y\oplus a_j=a_i y⊕aj=ai,这让我们联想到 F W T FWT FWT。
将 a a a 中的各个元素丢进桶 t t t 里面,对于所有 j ∈ [ 0 , 2 C ) j\in[0,2^C) j∈[0,2C),按照二进制 1 1 1 的个数归类,也按照类丢进桶里。然后对于每个类与 t t t 进行 F W T FWT FWT。在判断时,只需要判断下标 a i a_i ai 的数是否大于 0 0 0 即可。
时间复杂度为 O ( C 2 ⋅ 2 C ) O(C^2\cdot2^C) O(C2⋅2C)
F W T FWT FWT 数组记得开 long long,可能需要一些卡常技巧。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int C=18,N=1e5+1;
int c,n;
char s[C];
ll a[N],t[1<<C],b[C+1][1<<C],cnt[1<<C];
void fwt_xor(ll a[],int fl)
{
int n=1<<c;
for(int i=2;i<=n;i<<=1){
for(int j=0;j<n;j+=i){
for(int k=j;k<j+i/2;k++){
ll x=a[k]+a[k+i/2],y=a[k]-a[k+i/2];
a[k]=x>>(fl==-1),a[k+i/2]=y>>(fl==-1);
}
}
}
}
int main()
{
scanf("%d%d",&c,&n);
for(int i=0;i<(1<<c);i++) cnt[i]=cnt[i>>1]+(i&1),b[cnt[i]][i]=1;
for(int i=1;i<=n;i++){
scanf("%s",s);
for(int j=0;j<c;j++) a[i]=(a[i]<<1)|(s[j]=='G');
t[a[i]]++;
}
fwt_xor(t,1);
for(int i=0;i<=c;i++){
fwt_xor(b[i],1);
for(int j=0;j<(1<<c);j++) b[i][j]=b[i][j]*t[j];
fwt_xor(b[i],-1);
}
for(int i=1;i<=n;i++){
for(int j=c;j>=0;j--){
if(b[j][a[i]]){
printf("%d\n",j);
break;
}
}
}
}