[HZOI 2016]我们爱数数

[HZOI 2016]我们爱数数

题目大意:

一张圆桌,每个位置按顺时针从\(1\)\(n\)编号。有\(n\)个人,编号从\(1\)\(n\)。如果编号为\(i\)的人坐到了编号为\(i\)的位置或坐到了与编号为\(i\)的位置相邻的位置,这个人就会感到开心,反之这个人会感到沮丧。求有多少种安排坐位的方法,使所有人都入座,并且使得至少\(k\)个人开心。

思路:

\(f_{i,j,s}\)表示前\(i\)个人,\(j\)个人开心,目前最后两个位置的状态为\(s\)的方案数。

枚举前两个人的位置,就可以化环为链,直接DP就可以求出\(f\)的值。

\(g_i\)表示\((n-i)!\sum f_{i,j,s}\)\(g_i=\sum {j\choose i}ans_j\)。故\(ans\)可以通过\(g_i\)推出。

参考题解

源代码:

#include<cstdio>
#include<cctype>
#include<cstring>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
typedef long long int64;
const int N=1001;
const int mod=1e9+7;
int p1[4]={0,0,1,2},p2[4]={0,1,2,3};
int n,m,fac[N],ifac[N],f[N][N][4],g[N];
bool v[N];
void exgcd(const int &a,const int &b,int &x,int &y) {
    if(!b) {
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,y,x);
    y-=a/b*x;
}
inline int inv(const int &x) {
    int ret,tmp;
    exgcd(x,mod,ret,tmp);
    return ((ret%mod)+mod)%mod;
}
inline int C(const int &n,const int &m) {
    if(m>n) return 0;
    return (int64)fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int main() {
    n=p1[1]=getint(),m=getint();
    for(register int i=fac[0]=1;i<=n;i++) {
        fac[i]=(int64)fac[i-1]*i%mod;
    }
    ifac[n]=inv(fac[n]);
    for(register int i=n;i>=1;i--) {
        ifac[i-1]=(int64)ifac[i]*i%mod;
    }
    for(register int i=0;i<4;i++) {
        const int &a=p1[i];
        v[a]=true;
        for(register int j=0;j<4;j++) {
            const int &b=p2[j];
            if(a&&b&&a==b) continue;
            v[b]=true;
            memset(f,0,sizeof f);
            f[2][(!!a)+(!!b)][(v[3]<<1)|v[2]]=1;
            for(register int i=2;i<n;i++) {
                for(register int j=0;j<=i;j++) {
                    for(register int k=0;k<4;k++) {
                        if(!f[i][j][k]) continue;
                        (f[i+1][j][k>>1]+=f[i][j][k])%=mod;
                        (f[i+1][j+1][2+(k>>1)]+=f[i][j][k])%=mod;
                        if(!(k&1)) (f[i+1][j+1][k>>1]+=f[i][j][k])%=mod;
                        if(!((k>>1)&1)) (f[i+1][j+1][1]+=f[i][j][k])%=mod;
                    }
                }
            }
            const int t=((v[1]<<1)+v[n]);
            for(register int i=1;i<=n;i++) {
                for(register int k=0;k<4;k++) {
                    if(k&t) continue;
                    (g[i]+=(int64)f[n][i][k]*fac[n-i]%mod)%=mod;
                }
            }
            v[b]=false;
        }
        v[a]=false;
    }
    for(register int i=n;i;i--) {
        for(register int j=i+1;j<=n;j++) {
            g[i]=((g[i]-(int64)g[j]*C(j,i)%mod)%mod+mod)%mod;
        }
    }
    int ans=0;
    for(register int i=m;i<=n;i++) (ans+=g[i])%=mod;
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/skylee03/p/9365444.html