1. 程式人生 > 其它 >2020 ICPC 瀋陽站 M. United in Stormwind

2020 ICPC 瀋陽站 M. United in Stormwind

FWT SOS

題意:有m個問題,n份長度為m的答卷,每個答案都是A或者B

現在要任選一個問題的子集,如果大於等於k對答卷在該子集上答案有不同,就說這個子集是可辯別的

求可辯別的子集數量

首先將答卷轉為01串,那麼兩個答卷相同就等價於兩個01串異或為0

對於一個問題的子集S,先考慮計算異或等於S的01串對數量,即

\[\rm F(S)= \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}[ans_i \oplus ans_j=S] \]

我們用一個桶陣列num[S]表示等於S的01串數量,那麼F可以寫為

\[\rm F(S)= \dfrac{1}{2}\sum\limits_{i\oplus j=S}num[i]*num[j] \]

這是FWT的標準形式,可以在\(\rm O(m2^m)\)

的時間求出F陣列

我們實際要求的集合T, 只要和S有交,就可以被F(S)貢獻

因而有

\[\rm G(T) = \sum\limits_{S \cap T\not=\emptyset}F(S) \]

簡單容斥一下,有

\[\rm G(T) = \dfrac{n*n}{2}-\sum\limits_{S \cap T=\emptyset}F(S) \]

這裡的\(S\cap T=\emptyset\) 等價於 \(S\subseteq U-T\),列舉子集\(3^m\)轉移不可取

實際上這是在CF上考多次的SOS模型,可以用DP在\(\rm O(m2^m)\)

設dp[S][i]表示做完前i位,S的答案,轉移考慮第i位是0還是1

如果第i位是0,直接從前面轉移過來,即dp[S][i]=dp[S][i-1]

如果第i位是1,那麼考慮這位可以為0或1,即dp[S][i]=dp[S][i-1]+dp[S^(1<<i)][i-1]

實際中第二維可以被壓掉,因為從小往大列舉S時,轉移的物件總是前面的

這樣就做完了,\(ans=\sum\limits_{S}[n*n-2G(s)\geq2k]\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

int rd(){
  int ret=0,f=1;char c;
  while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
  while(isdigit(c))ret=ret*10+c-'0',c=getchar();
  return ret*f;
}

// const int MOD = 998244353,INV2=499122177;
const double
Cor[2][2]={{1,0},{1,1}},
Cand[2][2]={{1,1},{0,1}},
Cxor[2][2]={{1,1},{1,-1}},
ICor[2][2]={{1,0},{-1,1}},
ICand[2][2]={{1,-1},{0,1}},
ICxor[2][2]={{0.5,0.5},{0.5,-0.5}};

void FWT(double *f,const double c[2][2],int n){
  for(int len=1;len<n;len<<=1)
    for(int p=0;p<n;p+=len+len)
      for(int i=p;i<p+len;i++){
        double sav=f[i];
        f[i]=(c[0][0]*f[i]+c[0][1]*f[i+len]);
        f[i+len]=(c[1][0]*sav+c[1][1]*f[i+len]);
      }
}

void bitmul(double *f,double *g,const double c[2][2],const double ic[2][2],int n){
  FWT(f,c,n);//FWT(g,c,n);
  for(int i=0;i<n;i++) f[i]*=f[i];
  FWT(f,ic,n);
}

const int MAXN = 2200006;
int n,m,k;

double f[MAXN];
char s[50];
signed main(){
  n=rd();m=rd();k=rd();
  for(int i=1;i<=n;i++){
    scanf("%s",s+1);
    int tmp=0;
    for(int j=1;j<=m;j++){
      if(s[m-j+1]=='A') tmp|=(1<<(j-1));
    }
    f[tmp]+=1.0;
  }
  bitmul(f,f,Cxor,ICxor,1<<m);
  for(int i = 0; i < m; i++)
    for(int j = 0; j < (1<<m); j++)
        if(j & (1 << i)) f[j] += f[j ^ (1 << i)];
  int ans=0;
  for(int i=0;i<(1<<m);i++){
    if(n*n-(long long)(f[i]+0.5)>=2*k) ans++;
  }
  cout<<ans;
  return 0;
}
未經許可,禁止搬運。