• bzoj4665小w的喜糖 dp+容斥


    4665: 小w的喜糖

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 120  Solved: 72
    [Submit][Status][Discuss]

    Description

    废话不多说,反正小w要发喜糖啦!!

    小w一共买了n块喜糖,发给了n个人,每个喜糖有一个种类。这时,小w突发奇想,如果这n个人相互交换手中的糖,那会有多少种方案使得每个人手中的糖的种类都与原来不同。

    两个方案不同当且仅当,存在一个人,他手中的糖的种类在两个方案中不一样。

    Input

    第一行,一个整数n

    接下来n行,每行一个整数,第i个整数Ai表示开始时第i个人手中的糖的种类

    对于所有数据,1≤Ai≤k,k<=N,N<=2000

    Output

    一行,一个整数Ans,表示方案数模1000000009

    Sample Input

    6
    1
    1
    2
    2
    3
    3

    Sample Output

    10

    f[i][j]表示前i种糖,j个人拿到的糖相同,剩下的糖果先不发给人
    转移式还是挺好推的
    把每种糖看成不同,b[i]表示第i种糖的个数
    每新增加一种糖,枚举现在几个人拿到自己的糖 一个人拿到自己的糖要在所有同种糖中选择
    转移完之后,所有f[i][j]*=fac[n-j]表示剩下的糖随意分给其他人,不保证不分到自己的糖
    所以f[i][j]就变成了至少j个人拿到相同的糖,容斥一下
    最后,因为把每种糖的个体视为不同,需要 /fac[b[i]]
    推荐blog
    http://www.cnblogs.com/zj75211/p/8035076.html

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    #define N 2005
    #define mod 1000000009
    using namespace std;
    int n,m,f[N][N],a[N],b[N],fac[N],inv[N],c[N][N],sum[N];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++){
            if(a[i]!=a[i-1])m++;
            b[m]++;
        }
        for(int i=1;i<=m;i++)
        sum[i]=sum[i-1]+b[i];
        for(int i=0;i<=2000;i++)
        c[i][i]=c[i][0]=1;
        for(int i=1;i<=2000;i++)
        for(int j=1;j<i;j++)
        c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
        fac[0]=fac[1]=inv[0]=inv[1]=1;
        for(int i=2;i<=2000;i++){
            fac[i]=1ll*fac[i-1]*i%mod;
            inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
        }
        for(int i=2;i<=2000;i++)inv[i]=1ll*inv[i]*inv[i-1]%mod;
        f[0][0]=1;
        for(int i=1;i<=m;i++)
        for(int j=0;j<=sum[i-1];j++)
        for(int k=0;k<=b[i];k++)
        f[i][j+k]=(f[i][j+k]+1ll*f[i-1][j]*c[b[i]][k]%mod*fac[b[i]]%mod*inv[b[i]-k]%mod)%mod;
        ll ans=0;
        for(int i=n;i>=0;i--)
        ans=(ans+1ll*((n-i)&1?-1:1)*f[m][i]*fac[n-i])%mod;
        for(int i=1;i<=m;i++)ans=ans*inv[b[i]]%mod;
        ans<0?ans+=mod:1;
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    [LeetCode 题解]: Triangle
    [LeetCode 题解]: pow(x,n)
    [LeetCode 题解]: plusOne
    [LeetCode 题解]: ZigZag Conversion
    error: field 'b' has imcomplete type
    两个分数的最小公倍数
    DDR工作原理
    流水线技术原理和Verilog HDL实现
    FPGA主要应用
    提高器件工作的速度
  • 原文地址:https://www.cnblogs.com/wsy01/p/8036452.html
Copyright © 2020-2023  润新知