zoukankan      html  css  js  c++  java
  • Luogu P5339 [TJOI2019]唱、跳、rap和篮球

    这看题目一眼容斥,令聊唱、跳、rap和篮球的组数至少(x)的方案数为(g(x))

    那么显然(Ans=sum_{i=0}^{min(lfloor frac{n}{4} floor,a,b,c,d)} (-1)^ig(i)),考虑如何计算(g(x))

    先考虑放下给定的(x)组,显然它们的顺序是唯一的,也就是确定了第一个就能确定后面的

    相当于现在连续的(4)个位置变成了选第一个位置,因此方案数是(C_{n-3x}^x)

    那么剩下的(n-4x)个数怎么办,可重全排列

    [sum_{i=0}^{a-x}sum_{j=0}^{b-x}sum_{p=0}^{c-x}sum_{q=0}^{d-x} [i+j+p+q=n-4x]frac{(n-4x)!}{i!j!p!q!} ]

    [=(n-4x)! imessum_{i=0}^{a-x}sum_{j=0}^{b-x}sum_{p=0}^{c-x}sum_{q=0}^{d-x}[i+j+p+q=n-4x] frac{1}{i!} imes frac{1}{j!} imes frac{1}{p!} imes frac{1}{q!} ]

    显然后面的部分是一个卷积的形式,由于数据范围小直接暴力卷即可

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #define RI register int
    #define CI const int&
    #define Ms(f,x) memset(f,x,sizeof(f))
    using namespace std;
    const int N=1005,mod=998244353;
    int n,A,B,C,D,a[N],b[N],c[N],d[N],fact[N],inv[N],len,ret,ans,lim;
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    inline void dec(int& x,CI y)
    {
    	if ((x-=y)<0) x+=mod;
    }
    inline int quick_pow(int x,int p=mod-2,int mul=1)
    {
    	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
    }
    inline void init(CI n)
    {
    	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
    	for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
    }
    inline int Comb(CI n,CI m)
    {
    	return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
    }
    inline void conv(int *a,int& la,int *b,CI lb,CI n)
    {
    	RI i,j; static int r[N]; Ms(r,0);
    	for (i=0;i<=la;++i) for (j=0;j<=lb;++j)
    	if (i+j<=n) inc(r[i+j],1LL*a[i]*b[j]%mod);
    	la=min(n,la+lb); for (i=0;i<=la;++i) a[i]=r[i];
    }
    inline int calc(CI n,CI x)
    {
    	RI i; Ms(a,0); Ms(b,0); Ms(c,0); Ms(d,0);
    	for (i=0;i<=A-x;++i) a[i]=inv[i]; for (i=0;i<=B-x;++i) b[i]=inv[i];
    	for (i=0;i<=C-x;++i) c[i]=inv[i]; for (i=0;i<=D-x;++i) d[i]=inv[i];
    	len=A-x; conv(a,len,b,B-x,n); conv(a,len,c,C-x,n); conv(a,len,d,D-x,n); return a[n];
    }
    int main()
    {
    	scanf("%d%d%d%d%d",&n,&A,&B,&C,&D); init(n);
    	RI i; lim=min(n/4,min(min(A,B),min(C,D)));
    	for (i=0;i<=lim;++i) ret=1LL*Comb(n-3*i,i)*fact[n-4*i]%mod*calc(n-4*i,i)%mod,
    	i&1?(dec(ans,ret),0):(inc(ans,ret),0); return printf("%d",ans),0;
    }
    
  • 相关阅读:
    Java基础—ArrayList源码浅析
    Java基础——类加载机制
    Java基础——常用类之日期时间类
    Java基础——反射
    栈队列例题3:使用两个栈实现一个队列
    栈队列例题2:SetOfStack放盘子
    栈队列例题1:设置带最小值的栈
    链表例题6:检查链表中的数据是否回文
    队列的实现
    栈的实现
  • 原文地址:https://www.cnblogs.com/cjjsb/p/12217219.html
Copyright © 2011-2022 走看看