多校赛 count

我们爱数数 (counting) TankEngineer 是数数高手,每天早上的乐趣是倒背圆周率。 TankEngineer 的家里有一张圆桌,每个位置按顺时针从 1 到 N 编号,差的绝对值为 1 的两个位置 相邻。特别的,编号为 N 的位置与编号为 1 的位置相邻。 他的家里某天来了 N 个人,编号从 1 到 N。如果编号为 i 的人坐到了编号为 i 的位置或坐到了与编 号为 i 的位置相邻的位置,这个人就会感到开心,反之这个人会感到沮丧。 于是 TankEngineer 想知道,有多少种安排坐位的方法,使所有人都入座,并且使得至少 K 个人开 心。 输入格式 第一行,包含两个整数 N, K。 输出格式 第一行,包含一个整数,表示合法方案的数量。因为数值太大,你只需要输出结果除以 (109 +7) 的 余数。 输入输出样例 输入 输出 4 4 9 样例解释 合法的方案有{1,2,3,4}, {1,2,4,3}, {1,3,2,4}, {2,1,3,4}, {2,1,4,3}, {2,3,4,1}, {4,1,2,3}, {4,2,3,1}, {4,3,2,1}这九种。 数据范围 • 对于 20% 的数据,3 ≤ K ≤ N ≤ 10。 • 对于另外 40% 的数据,3 ≤ K ≤ N ≤ 20。 • 对于 100% 的数据,3 ≤ K ≤ N ≤ 1,000。

对其先dp,再容斥。

//#pragma GCC optimize("-Ofast")
#include<bits/stdc++.h>
#define N 1007
#define LL long long
#define mo 1000000007
#define pan(x) (x>1?x*2-4:x*2)
#define rev(x) (x>1?(x&1)*2+1:(x&1)*2)
using namespace std;
LL fac[N],dp[2][4][4][N];
int cnt,usd[7];
void dfs(int x){
    if (x==4) {
        cnt=0;
        for (int i=0;i<=4;i++) cnt+=usd[i];
        dp[0][usd[0]*2+usd[1]][usd[3]*2+usd[4]][cnt]++;
        return;
    }
    dfs(x+1);
    for (int i=x-1;i<=x+1;i++) if (!usd[i]) {
        usd[i]=1; dfs(x+1); usd[i]=0;
    }
}
LL ans[N],anw;
int last,now,sg,n,k;
void Mo(LL &X){X<mo?0:X-=mo;}
void Mo2(LL &X){X<0?X+=mo:0;}
//namespace Lxf{
//  int dp[2][1<<20|7][23];
//  LL anw[21]={146326063,815973092,915753998,600280495,137677515,450305684,64421590,402761682,449445773,215161119,505685262,896311088,955416925,457272986,211789501,304437609,869722750,177100439,9159959,285649,15129};
//  void bao() {
//      if (n==20) {
//          printf("%lld
",anw[k]); exit(0);}
//      memset(dp,0,sizeof dp); ans=0;
//      for (int i=3;i<n;i++) dp[0][1<<i-1][0]++;
//      
//      dp[0][1][1]=1; dp[0][2][1]=1; dp[0][1<<n-1][1]=1;
//      int sta=(1<<n);
//      for (int i=2;i<=n;i++) {
//          last=i&1; now=1^last;
//          memset(dp[now],0,sizeof dp[now]);
//          for (int st=0;st<sta;st++) {
//          for (int p=0;p<i;p++) {
//           if (!dp[last][st][p]) continue; 
//           for (int k=1;k<=n;k++) {
//               if ((st>>k-1)&1) continue;
//            if (abs(k-i)<2||(i==n&&k==1)) Mo(dp[now][st|(1<<k-1)][p+1]+=dp[last][st][p]);
//            else  Mo(dp[now][st|(1<<k-1)][p]+=dp[last][st][p]);
//          }
//         }
//        }
//    }
//    for (int st=0;st<sta;st++) 
//     for (int p=k;p<=n;p++) 
//      Mo(ans+=dp[now][st][p]);
//    printf("%d",ans);//cerr<<ans<<endl;
//    exit(0);
//  }
//}
LL ni[N];
LL qsm(LL x,LL y){
    static LL anw;
    for (anw=1;y;y>>=1,x=x*x%mo) if (y&1) anw=anw*x%mo;
    return anw;
}
LL C(int x,int y){
    return fac[x]*ni[y]%mo*ni[x-y]%mo;
}
signed main () {
    freopen("counting.in","r",stdin);
    freopen("counting.out","w",stdout);
    scanf("%d%d",&n,&k);
    fac[0]=1; 
    for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mo;
    ni[n]=qsm(fac[n],mo-2);
    for (int i=n;i  ;i--) ni[i-1]=ni[i]*i%mo;
    if (k==0) return printf("%lld
",fac[n]),0;
    dfs(1);
//    for(int i = 0; i < 4; ++i) {
//        for(int j = 0; j < 4; ++j)    
//        printf("%d ", dp[0][i][j][3]);
//        puts("");
//    }
    for (int i=4;i<=n;i++) {
     last=i&1; now=1^last;
     memset(dp[now],0,sizeof dp[now]);    
      for (int ins=0;ins<i;ins++) {
        for (int st=0;st<4;st++) {
            for (int ed=0;ed<4;ed++) {
               sg=pan(ed); 
               if (ins==3) ;
               if (!(ed&2))  (dp[now][st][sg  ][ins+1]+=dp[last][st][ed][ins])%=mo;
               if (!(sg&2))  (dp[now][st][sg+2][ins+1]+=dp[last][st][ed][ins])%=mo;
               if (!(sg&1))  (dp[now][st][sg+1][ins+1]+=dp[last][st][ed][ins])%=mo; 
            }
        }
      }
      for (int ins=0;ins<=i;ins++)
      for (int st=0;st<4;st++) {
        for (int ed=0;ed<4;ed++) 
          (dp[now][st][pan(ed)][ins]+=dp[last][st][ed][ins])%=mo;
      } 
    }
    
    for (int i=1;i<=n;i++) 
     for (int st=0;st<4;st++)
      for (int ed=0;ed<4;ed++) {
        if (ed&st) continue;
       Mo(ans[i]+=dp[now][st][ed][i]);
     }
    for (int i=n;i>=k;i--) {
     ans[i]=ans[i]*fac[n-i]%mo;
     for (int j=i+1;j<=n;j++) 
      ans[i]-=ans[j]*C(j,i)%mo,Mo2(ans[i]); 
      Mo(anw+=ans[i]);
    }
    printf("%lld
",anw); 
    return 0;
}
原文地址:https://www.cnblogs.com/rrsb/p/9867301.html