唱、跳、rap和篮球

唱、跳、rap和篮球

大中锋的学院要组织学生参观博物馆,要求学生们在博物馆中排成一队进行参观。他的同学可以分为四类:一部分最喜欢唱、一部分最喜欢跳、一部分最喜欢rap,还有一部分最喜欢篮球。如果队列中\(k,k + 1,k+1,k + 2,k+2,k + 3,k+3\)位置上的同学依次,最喜欢唱、最喜欢跳、最喜欢rap、最喜欢篮球,那么他们就会聚在一起讨论蔡徐坤。大中锋不希望这种事情发生,因为这会使得队伍显得很乱。大中锋想知道有多少种排队的方法,不会有学生聚在一起讨论蔡徐坤。两个学生队伍被认为是不同的,当且仅当两个队伍中至少有一个位置上的学生的喜好不同。由于合法的队伍可能会有很多种,种类数对998244353取模。

Input

输入数据只有一行。每行55个整数,第一个整数n,代表大中锋的学院要组织多少人去参观博物馆。接下来四个整数a、b、c、d,分别代表学生中最喜欢唱的人数、最喜欢跳的人数、最喜欢rap的人数和最喜欢篮球的人数。保证\(a+b+c+d \ge n\)。

Output

每组数据输出一个整数,代表你可以安排出多少种不同的学生队伍,使得队伍中没有学生聚在一起讨论蔡徐坤。结果对998244353取模。

Sample Input 1

4 4 3 2 1

Sample Output 1

174

Sample Input 2

996 208 221 132 442

Sample Output 2

442572391

思路:

\(令f_x=(n-4x)!\sum\limits_{i=0}^a\sum\limits_{j=0}^b\sum\limits_{k=0}^c\sum\limits_{w=0}^d[a+b+c+d=n-4x]\frac{1}{i!j!k!w!}\)

\(ans=\sum\limits_{i=0}^{min\{a,b,c,d,\lfloor\frac{n}{4}\rfloor\}}(-1)^i(^{n-3i}_{\ \ \ \ i})f_i\)

证明:

后面把讨论蔡徐坤的段叫\(jntm\)

\(考虑枚举jntm的数量,进行容斥\)

\(假设f_i为在去掉jntm后的方案数\)

\(\because 每个jntm长度4\)

\(\therefore 有C_{n-3i}^i的方案数\)

\(\therefore ans=\sum\limits_{i=0}^{min\{a,b,c,d,\lfloor\frac{n}{4}\rfloor\}}(-1)^i(^{n-3i}_{\ \ \ \ i})f_i\)

\(考虑枚举剩下的位置中有多少个j,n,t,m\)

\(f_x=\sum\limits_{i=0}^a\sum\limits_{j=0}^b\sum\limits_{k=0}^c\sum\limits_{w=0}^d[a+b+c+d=n-4x]\frac{(n-4x)!}{i!j!k!w!}=(n-4x)!\sum\limits_{i=0}^a\sum\limits_{j=0}^b\sum\limits_{k=0}^c\sum\limits_{w=0}^d[a+b+c+d=n-4x]\frac{1}{i!j!k!w!}\)

\(发现可以NTT优化卷积,时间复杂度O(n^2log_2n)\)

\(\mathfrak{Talk\ is\ cheap\ ,show\ you\ the\ code.}\)

#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
# define read read1<int>()
# define Type template<typename T>
Type inline T read1(){
    T n=0;
    char k;
    bool fl=0;
    do (k=getchar())=='-'&&(fl=1);while('9'<k||k<'0');
    while(47<k&&k<58)n=(n<<3)+(n<<1)+(k^48),k=getchar();
    return fl?-n:n;
}
inline int r(int mod){
    int n=0;
    char k;
    bool fl=0;
    do (k=getchar())=='-'&&(fl=1);while('9'<k||k<'0');
    while(47<k&&k<58)n=(10ll*n+(k^48))%mod,k=getchar();
    return fl?mod-n:n;
}
# define f(i,l,r) for(int i=(l);i<=(r);++i)
# define fre(k) freopen(k".in","r",stdin);freopen(k".ans","w",stdout)
# define ll int64_t
class Array{
    private:
        vector<int>a;
    public:
        Array(const int size,const int f):a(size,f){}
        void push(int n){a.push_back(n);}
        Array(int* l=NULL,int* r=NULL){while(l!=r)push(*l),++l;}
        inline int size(){return a.size();}
        inline int& operator [] (const int x){return a[x];}
        void resize(int n){a.resize(n);}
        void clear(){a.clear();}
        void swap(){reverse(a.begin(),a.end());}
        int& top(){return a[a.size()-1];}
        void pop(){a.pop_back();}
        Array& operator %= (const int k){
            for(int i=0;i<size();++i)
                a[i]%=k;
            return *this;
        }
};
const int mod=998244353,g=3,inv2=499122177;
Array operator -(Array a,Array b){
    int N=a.size(),M=b.size();
    Array t;
    for(int i=0;i<N||i<M;++i){
        t.push((i<N?a[i]:0)-(i<M?b[i]:0));
        if(t.top()<0)t.top()+=mod;
    }
    return t;
}
Array operator +(Array a,Array b){
    int N=a.size(),M=b.size();
    Array t;
    for(int i=0;i<N||i<M;++i){
        t.push((i<N?a[i]:0)+(i<M?b[i]:0));
        if(t.top()>=mod)t.top()-=mod;
    }
    return t;
}
Array operator *(Array a,int n){
    int N=a.size();
    for(int i=0;i<N;a[i]=(ll)a[i]*n%mod,++i);
    return a;
}
Array operator *(int n,Array a){return a*n;}
int qkpow(int b,int m,int mod){
    int tem=b,ans=1;
    for(;m;m>>=1,tem=(ll)tem*tem%mod)
        if(m&1)ans=(ll)ans*tem%mod;
    return ans;
}
int* NTT(const int len,Array& a,const bool Ty,int* r=NULL){
    if(!r){
        r=new int[len];
        r[0]=0;int L=log2(len);
        f(i,0,len-1)
            r[i]=(r[i>>1]>>1)|((i&1)<<L-1);
    }
    f(i,0,len-1)
        if(i<r[i])swap(a[i],a[r[i]]);
    for(int i=1;i<len;i<<=1){
        int T=qkpow(Ty?g:332748118,(mod-1)/(i<<1),mod);
        for(int W=i<<1,j=0;j<len;j+=W){
            ll omega=1;
            for(int k=0;k<i;++k,omega=omega*T%mod){
                ll x(a[j+k]),y(omega*a[i+j+k]%mod);
                a[j+k]=x+y;(a[j+k]>=mod)&&(a[j+k]-=mod);
                a[i+j+k]=x-y+mod;(a[i+j+k]>=mod)&&(a[i+j+k]-=mod);
            }
        }
    }
    return r;
}
Array operator * (Array x,Array y){
    int n=x.size()-1,m=y.size()-1;
    int limit=1;
    while(limit<=n+m)limit<<=1;
    Array ans;
    x.resize(limit+1);
    y.resize(limit+1);
    int *r;
    r=NTT(limit,x,1);
    NTT(limit,y,1,r);
    f(i,0,limit)x[i]=(ll)x[i]*y[i]%mod;
    NTT(limit,x,0,r);
    int tem=qkpow(limit,mod-2,mod);
    f(i,0,n+m)ans.push((ll)x[i]*tem%mod);
    return ans;
}
Array& operator *= (Array& x,Array y){
    return x=x*y;
}
void Rev(Array &x,Array y){
    int n=x.size()-1,m=y.size()-1;
    int limit=1;
    while(limit<=n+m)limit<<=1;
    Array ans;
    x.resize(limit+1);
    y.resize(limit+1);
    int *r;
    r=NTT(limit,x,1);
    NTT(limit,y,1,r);
    f(i,0,limit)x[i]=(ll)(2ll-(ll)x[i]*y[i]%mod+mod)%mod*y[i]%mod;
    NTT(limit,x,0,r);
    int tem=qkpow(limit,mod-2,mod);
    f(i,0,n+m)x[i]=(ll)x[i]*tem%mod;
    x.resize(n+m+1);
}
Array Inv(Array a){
    int N=a.size();
    // printf("%d\n",N);
    if(N==1)return Array(1,qkpow(a[0],mod-2,mod));
    Array b=a;b.resize(N+1>>1);
    b=Inv(b);b.resize(N);
    Rev(a,b);
    a.resize(N);
    return a;
}
Array operator / (Array x,Array y){
    int N=x.size()-1,M=y.size()-1;
    if(N<M)return Array(1,0);
    x.swap();y.swap();
    y.resize(N-M+1);
    x*=Inv(y);
    x.resize(N-M+1);
    x.swap();
    return x;
}
Array sqrt(Array x){
    int N=x.size();
    if(N==1)return Array(1,1);
    Array y=x;
    y.resize(N+1>>1);
    y=sqrt(y);
    y.resize(N);
    Array z=Inv(y);
    x*=z;x.resize(N);
    return inv2*(y+x);
}
Array diff(Array x){
    for(int i=0;i+1<x.size();++i)
        x[i]=(ll)x[i+1]*(i+1)%mod;
    x.pop();
    return x;
}
Array integral(Array x){
    for(int i=x.size();--i;)
        x[i]=(ll)x[i-1]*qkpow(i,mod-2,mod)%mod;
    x[0]=0;
    return x;
}
Array ln(Array x){
    int N=x.size();
    x=integral(diff(x)*Inv(x));
    x.resize(N);
    return x;
}
Array exp(Array x){
    int N=x.size();
    if(N==1)return Array(1,1);
    Array y=x;
    y.resize(N+1>>1);
    y=exp(y);
    y.resize(N);
    y[0]=1;
    ++x[0];
    y*=(x-ln(y));
    y.resize(N);
    return y;
}
int fac[1001],n(read),a(read),b(read),c(read),d(read),inv[1001];
Array x,y,z,w;
int C(int n,int m){return (ll)fac[n]*inv[n-m]%mod*inv[m]%mod;}
int sigma(int l){
    int ans=C(n-l*3,l),A=a-l,B=b-l,C=c-l,D=d-l;
    x.clear();y.clear();z.clear();w.clear();
    f(i,0,A)x.push(inv[i]);
    f(i,0,B)y.push(inv[i]);
    f(i,0,C)z.push(inv[i]);
    f(i,0,D)w.push(inv[i]);
    int limit=1;
    while(limit<=A+B+C+D)limit<<=1;
    x.resize(limit+1);
    y.resize(limit+1);
    z.resize(limit+1);
    w.resize(limit+1);
    int *r;
    r=NTT(limit,x,1);
    NTT(limit,y,1,r);
    NTT(limit,z,1,r);
    NTT(limit,w,1,r);
    f(i,0,limit)x[i]=(ll)x[i]*y[i]%mod*z[i]%mod*w[i]%mod;
    NTT(limit,x,0,r);
    int tem=qkpow(limit,mod-2,mod);
    return (ll)ans*fac[n-(l<<2)]%mod*x[n-(l<<2)]%mod*tem%mod;
}
int main(){
    fac[1]=fac[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=1000;++i)
        inv[i]=qkpow(fac[i]=(ll)fac[i-1]*i%mod,mod-2,mod);
    int s=min(n>>2,min(a,min(b,min(c,d)))),ans=0;
    for(int i=0;i<=s;++i)
        if(i&1)ans=(ans-sigma(i)+mod)%mod;
        else ans=(ans+sigma(i))%mod;
    printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/SYDevil/p/12108575.html

时间: 2024-08-30 16:37:54

唱、跳、rap和篮球的相关文章

[TJOI2019]唱、跳、rap和篮球——NTT+生成函数+容斥

题目链接: [TJOI2019]唱.跳.rap和篮球 直接求不好求,我们考虑容斥,求出至少有$i$个聚集区间的方案数$ans_{i}$,那么最终答案就是$\sum\limits_{i=0}^{n}(-1)^i\ ans_{i}$ 那么现在只需要考虑至少有$i$个聚集区间的方案数,我们枚举这$i$个区间的起始点位置,一共有$C_{n-3i}^{i}$种方案(可以看作是刚开始先将每个区间后三个位置去掉,从剩下$n-3i$个位置中选出$i$个区间起点,然后再在每个起点后面加上三个位置). 那么剩下的$

[luogu5339] [TJOI2019]唱、跳、rap和篮球(容斥原理+组合数学)(不用NTT)

[luogu5339] [TJOI2019]唱.跳.rap和篮球(容斥原理+组合数学)(不用NTT) 题面 略 分析 首先考虑容斥,求出有i堆人讨论的方案. 可以用捆绑法,把每堆4个人捆绑成一组,其他人每个人一组.这样一共有\(n-3i\)组(这些组可以被看成相同的点). 我们从中选出n-4i个点,这些点展开成1个人,其他\(i\)个点展开成4个人.那么方案数就是\(C_{n-3i}^{n-4i}\) 由于\(i\)堆人的喜好已经确定,最终答案为\(\sum_{i=0}^n (-1)^i \ti

[TJOI2019]唱、跳、rap和篮球

题目 套路题啊 发现正向计数不太好记,考虑容斥 考虑求至少有\(i\)段连续四个位置是不合法的,容斥系数显然是\((-1)^i\) 我们先选出这样的\(i\)段长度为\(4\)的区间来 我们考虑分配一下空格,问题就等价于把\(n-4i\)个空格分到\(i+1\)组里,插板一下就能知道答案是\(\binom{n-3i}{i}\) 考虑剩下的\(n-4i\)个空格,我们现在需要把这些空格填满 先来考虑一下这四种分别填了\(a,b,c,d\)个方案数是多少 这\(n-4i\)个位置直接去排列,是\((

【题解】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\)个元素 我们把问题转换成一个

[TJOI2019]唱、跳、rap和篮球——容斥原理+生成函数

先附一组sd图 然后放上原题链接 注意,队伍不同指的是喜好不同,不是人不同 先想到\(DP\),然后你会发现并没有什么优秀的状态设计,然后我们考虑容斥 设\(lim\)表示选的癌坤组数的上限,\(f_i\)为先选出来\(i\)组剩下随便排的方案数,那么答案就是 \[\sum\limits_{i=0}^{lim}(-1)^i\times\ f_i\] 于是问题转化为了求\(f_i\).显然\(f_i\)可以表示为一个组合数再乘一个东西,具体来说组合数代表在\(n\)个同学中选\(i\)组癌坤的方案

[TJOI2019]唱,跳,rap,篮球(生成函数,组合数学,NTT)

算是补了个万年大坑了吧. 根据 wwj 的题解(最准确),设一个方案 \(S\)(不一定合法)的鸡你太美组数为 \(w(S)\). 答案就是 \(\sum\limits_{S}[w(S)=0]\). 用二项式定理:\(\sum\limits_{S}[w(S)=0]=\sum\limits_{S}(1-1)^{w(S)}=\sum\limits_{S}\sum\limits_{i\ge 0}(-1)^i\binom{w(S)}{i}=\sum\limits_{i\ge 0}(-1)^i\sum\l

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

题目 设\(f_i\)表示从\((a-4i,b-4i,c-4i,d-4i)\)中选\(n-4i\)个排队的方案数. 那么我们可以容斥,答案为\(\sum\limits_{i=0}^{lim}(-1)^i{n-3i\choose i}f_i\). 考虑一下这个\(f\),它就是四个指数型生成函数卷起来\((\sum\limits_{i=0}^a\frac{x^i}{i!})(\sum\limits_{i=0}^b\frac{x^i}{i!})(\sum\limits_{i=0}^c\frac{x^

js:面向对象,Document对象:查找元素对象,修改元素,事件

面向对象编程 面向对象的编程,那么是更符合人类所接触的世界的逻辑思维. 将一个系统划分为各个子系统,子系统又由各个模块构成,将每个模块,系统划分为一个个对象,给这些对象赋予某些角色(属性/功能/方法). 伪面向对象编程语言 ---> 面向对象编程语言 1.创建对象的方式 (1) 字面量的方式 //字面量的形式 var student = { name:"蔡徐坤", type:"练习生", like:"唱跳rap篮球", rap:functi

python之函数的传参形参的第三种动态参数*args和**kwargs

1. 位置/关键字传参的缺点 当给函数传入的参数数目不定时,之前的传参方式解决不了问题. def eat(food1,food2,food3): print(f'我请你吃:{food1},{food2},{food3}') eat('蒸羊羔','蒸熊掌','蒸鹿尾') 万能参数,动态参数. *args def eat(food1,food2,food3): print(f'我请你吃:{food1},{food2},{food3}') eat('蒸羊羔','蒸熊掌','蒸鹿尾','烧花鸭','烧企