[USACO23OPEN] Field Day S

题目描述

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 1iC) 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 1N, please compute the maximum
difference between team t t t and any other team.

翻译

N N N 个长度为 C C C 的由字符 Gc 组成的字符串。对于每个字符串,求出它与其他字符串不同位置数量的最大值。

题解

这里提供一个不同的思路。

首先把每个字符串压缩成一个数。数的范围显然是 [ 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(aiaj)

如果暴力做,时间复杂度是 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 yaj=ai,就是可行的。

观察关键条件: y ⊕ a j = a i y\oplus a_j=a_i yaj=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(C22C)

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;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值