Luogu P5339 [TJOI2019]唱、跳、rap和篮球

题目
(f_i)表示从((a-4i,b-4i,c-4i,d-4i))中选(n-4i)个排队的方案数。
那么我们可以容斥,答案为(sumlimits_{i=0}^{lim}(-1)^i{n-3ichoose i}f_i)
考虑一下这个(f),它就是四个指数型生成函数卷起来((sumlimits_{i=0}^afrac{x^i}{i!})(sumlimits_{i=0}^bfrac{x^i}{i!})(sumlimits_{i=0}^cfrac{x^i}{i!})(sumlimits_{i=0}^dfrac{x^i}{i!}))
(f_i)就是(x^i)的系数乘上((n-4i)!)
我们考虑分成两半,前面是((sumlimits_{i=0}^afrac{x^i}{i!})(sumlimits_{i=0}^bfrac{x^i}{i!})),后面是((sumlimits_{i=0}^cfrac{x^i}{i!})(sumlimits_{i=0}^dfrac{x^i}{i!}))
每次修改相当于(a,b,c,d)都减一,这样子修改是(O(n))的。求某一位的值可以直接暴力卷,也是(O(n))的。
所以就做到了(O(n^2))

#include<bits/stdc++.h>
using namespace std;
const int N=1007,P=998244353;
int n,a,b,c,d,inv[N],fac[N],ifac[N],f[N],g[N];
void inc(int &a,int b){a+=b,a=a>=P? a-P:a;}
void dec(int &a,int b){a-=b,a=a<0? a+P:a;}
int mul(int a,int b){return 1ll*a*b%P;}
int C(int n,int m){return mul(mul(fac[n],ifac[m]),ifac[n-m]);}
void mns(int n,int m,int *f)
{
    for(int i=0;i<=n;++i) dec(f[i+m],mul(ifac[i],ifac[m]));
    for(int i=0;i<m;++i) dec(f[i+n],mul(ifac[i],ifac[n]));
}
int cal(int n)
{
    int s=0;
    for(int i=0;i<=n;++i) inc(s,mul(f[i],g[n-i]));
    return mul(s,fac[n]);
}
int main()
{
    cin>>n>>a>>b>>c>>d;int i,j,ans=0,x;
    for(inv[1]=1,i=2;i<N;++i) inv[i]=mul(P-P/i,inv[P%i]);
    for(fac[0]=ifac[0]=i=1;i<N;++i) fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[i-1],inv[i]);
    for(i=0;i<=a;++i) for(j=0;j<=b;++j) inc(f[i+j],mul(ifac[i],ifac[j]));
    for(i=0;i<=c;++i) for(j=0;j<=d;++j) inc(g[i+j],mul(ifac[i],ifac[j]));
    ans=cal(n);
    for(i=1;i*4<=n&&a&&b&&c&&d;++i,--a,--b,--c,--d) mns(a,b,f),mns(c,d,g),x=mul(cal(n-i*4),C(n-i*3,i)),i&1? dec(ans,x):inc(ans,x);
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11726915.html