我们爱数数 (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; }