【题解】Luogu P5339 [TJOI2019]唱、跳、rap和篮球

原题传送门

这题zsy写的是\(O(n^2)\),还有NTT\(O(n^2\log n)\)的做法。我的是暴力,\(O(\frac{a b n}{4})\),足够通过

考虑设\(f(i)\)表示序列中至少有\(i\)组人讨论cxk的方案数

这样就珂以进行容斥,易知答案ans为:

$$ans=\sum_{i=0}^{Min(n/4,a,b,c,d)} (-1)^i f(i)$$

我们考虑如何计算\(f(i)\)

如果视讨论cxk的组为一个元素,则一共有\(n-3*i\)个元素

我们把问题转换成一个多重排列的方案数

多重排列的方案数求法:

现在有\(m\)个不同的元素,每个\(i\)元素有\(a_i\)个,那么方案数为

$$(\sum_{i=1}^m a_i)! \times \prod_{i=1}^m \frac{1}{a_i!}$$

那么我们只要暴力计算即可

#include <bits/stdc++.h>
#define ll long long 
#define N 2005
#define mod 998244353
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
inline int Min(register int a,register int b)
{
    return a<b?a:b;
}
int n,a,b,c,d,lim;
ll fac[N],inv[N],f[N],res,ans; 
int main()
{
    n=read(),a=read(),b=read(),c=read(),d=read();
    fac[0]=fac[1]=inv[0]=inv[1]=1;
    for(register int i=2;i<=n;++i)
        fac[i]=fac[i-1]*i%mod;
    for(register int i=2;i<=n;++i)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    for(register int i=2;i<=n;++i)
        inv[i]=inv[i]*inv[i-1]%mod;
    lim=Min(n>>2,Min(Min(a,b),Min(c,d)));
    for(register int x=0,v;x<=lim;++x)
    {
        v=(x&1)?-1:1;
        for(register int i=0;i<=n;++i)
            f[i]=0;
        for(register int i=0;i<=a-x;++i)
            for(register int j=0;j<=Min(n-4*x-i,b-x);++j)
                f[i+j]=(f[i+j]+inv[i]*inv[j])%mod;
        res=0;
        for(register int i=0;i<=c-x;++i)
            for(register int j=0;j<=Min(n-4*x-i,d-x);++j)
                res=(res+inv[i]*inv[j]%mod*f[n-4*x-i-j])%mod;
        res=res*fac[n-3*x]%mod*inv[x]%mod;
        ans=(ans+v*res)%mod;
    }
    write(ans<0?ans+mod:ans);
    return 0;
}
posted @ 2019-05-25 15:24  JSOI爆零珂学家yzhang  阅读(344)  评论(0编辑  收藏  举报