[容斥 狀壓DP] Atcoder ARC093 F - Dark Horse

wwwww比賽的時候題目看錯了

假設我們確定的1的位置,那麼接下來的每一輪,1都會和一段長度爲2的冪的區間裏,標號最小的人pk。

把1固定在1位置(求出最終方案數後乘上 2n 就是答案),那麼就相當於區間 [2,2][3,4][5,8][2n1+1,2n] 裏的最小值不在給出的集合中

考慮容斥,那麼就只要求出標號在集合 S 中的區間的最小值在給出的集合中,其他區間隨便放的方案數就可以了

ai 從大到小排序,令 fi,S 表示前 i 個人,集合 S 中的區間的最小值在 a 中的最小值

因爲把 ai 從大到小排序了,所以每次轉移只要算出之前用了多少人,乘個組合數就可以了

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int N=20,P=1e9+7;

int n,m,p,a[N],f[N][1<<16|5],fac[1<<16|5],inv[1<<16|5];

inline int C(int x,int y){
  if(x<y) return 0;
  return 1LL*fac[x]*inv[y]%P*inv[x-y]%P;
}

int pw[N];

inline void add(int &x,int y){
  (x+=y)%=P;
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  scanf("%d%d",&n,&m);
  pw[0]=1; for(int i=1;i<=n;i++) pw[i]=pw[i-1]*2; p=pw[n];
  fac[0]=1; for(int i=1;i<=p;i++) fac[i]=1LL*fac[i-1]*i%P;
  inv[1]=1; for(int i=2;i<=p;i++) inv[i]=1LL*(P-P/i)*inv[P%i]%P;
  inv[0]=1; for(int i=1;i<=p;i++) inv[i]=1LL*inv[i]*inv[i-1]%P;
  for(int i=1;i<=m;i++) scanf("%d",&a[i]);
  sort(a+1,a+1+m); int ans=0; f[m+1][0]=1;
  for(int i=m;i;i--){
    for(int S=0;S<(1<<n);S++) f[i][S]=f[i+1][S];
    for(int S=0;S<(1<<n);S++){
      int cnt=0;
      for(int j=1;j<=n;j++)
    if(S>>(j-1)&1) cnt+=pw[j-1];
      int rst=p-a[i]-cnt;
      for(int j=1;j<=n;j++)
    if(S>>(j-1)&1);else{
      if(pw[j-1]-1>rst) continue;
      add(f[i][S|(1<<j-1)],1LL*f[i+1][S]*C(rst,pw[j-1]-1)%P*fac[pw[j-1]]%P);
    }
    }
  }
  for(int i=0;i<(1<<n);i++){
    int cur=f[1][i],cnt=0,tot=0;
    for(int j=1;j<=n;j++)
      if((i>>(j-1))&1) cnt+=pw[j-1],tot++;
    cur=1LL*cur*fac[p-cnt-1]%P;
    if(tot&1) ans=(ans-cur)%P;
    else ans=(ans+cur)%P;
  }
  ans=1LL*ans*p%P;
  printf("%d\n",(ans+P)%P);
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章