ZJNU 2455 - Zapina (DP)

COCI 2019/2020 CONTEST #5 - Zapina

ZJNU 2455 - Zapina


题意

\(n\)个人分配\(n\)不同的工作。

\(n\)个人拍成一列,第\(i\)个人只有在被严格安排到\(i\)份工作时会感到快乐(不会在意是什么工作,只在意数量)。

至少有一人感到快乐的分配方案数量。

限制

\(1\leq n\leq 350\)




思路

考虑对立事件,事件“至少有一人快乐”的对立事件为“没有一个人感到快乐”

由于每份工作之间是相互独立的,所以安排方案总共有\(n^n\)

所以所求可以转化成“总方案数”减去“没有一个人感到快乐”的方案数


考虑没有一个人感到快乐的情况,进行动态规划

\(dp[i][j]\)表示\(i\)个人分配\(j\)份工作,“没有一个人感到快乐”的方案数

单独考虑最后一个人被安排到的工作数量\(k\),容易得到转移方程

\[dp[i][j]=dp[i][j]+C_{j}^{k}dp[i-1][j-k],\ k\neq i \]

表示\(i\)个人分配\(j\)份工作,可以由\(i-1\)个人分配\(j-k\)份工作转移而来,且第\(i\)个人被分配到的这\(k\)份工作可以用组合数\(C_j^k\)来得出方案数

注意这里的\(k\neq i\)的条件,表示第\(i\)个人不能分配到\(i\)份工作,否则就偷税

最后,从小到大枚举\(i,j,k\)即可

人数枚举区间\([1,n]\)

工作可以没有,枚举区间\([0,n]\)

\(i\)个人的工作需要注意转移方程\(j-k\)的范围,枚举区间\([0,j]\)




程序

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;

ll qpow(ll a,ll n)
{
    ll r=1;
    while(n)
    {
        if(n&1)
            r=(r*a)%mod;
        n>>=1;
        a=(a*a)%mod;
    }
    return r;
}

ll C[360][360],dp[360][360];

int main()
{
    int n;
    scanf("%d",&n);
    
    C[0][0]=C[1][0]=C[1][1]=1;
    for(int i=2;i<=n;i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=n;j++)
            for(int k=0;k<=j;k++)
            {
                if(k!=i)
                    dp[i][j]=(dp[i][j]+dp[i-1][j-k]*C[j][k])%mod;
            }
    
    printf("%lld\n",(qpow(n,n)-dp[n][n]+mod)%mod);
    
    return 0;
}

posted @ 2020-10-05 19:31  StelaYuri  阅读(195)  评论(0编辑  收藏  举报