[TJOI2019]唱、跳、rap和篮球

这么多人过没人写题解啊

那我就随便说说了嗷

这题第一步挺套路的,就是题目要求不能存在balabala的时候考虑正难则反,要求必须存在的方案数然后用总数减,往往更简单。

这个题呢直接要求存在发现还不咋好求,反正就是存在嘛我们就容斥好了。

呐,我们就枚举至少有多少段(唱跳rap篮球)。

假设有\(i\)段,那么枚举一下这\(i\)段的位置,这是\(\binom{n-3i}{i}\)的。

就相当于给定长度为\(n-3i\)的空格,选出\(i\)个空格为(唱跳rap篮球)的起始点就好了。

假设每种人分别有\(num[1]\)到\(num[4]\)个,把他们都\(-i\),就相当于求剩下这些人随意排列的方案数咯。

假设第\(i\)种人用了\(now[i]\)个,那么方案数为

\(\frac{(n-4i)!}{\prod now[j]!}\)。

这东西就是拿生成函数搞一搞就好了。

就是第一个人的生成函数是\(\sum\limits_{i=0}^{num[1]} \frac{x^i}{i!}\)。

把这四个生成函数乘一起,最后返回第\(n-4i\)项乘以\((n-4i)!\)就好啦。

贴个代码

#include <bits/stdc++.h>
#define N 1010
using namespace std;
typedef long long ll;
const int mod = 998244353 ;
int C[N][N],num[5],len[5],fac[N<<2],inv[N<<2],b[5][N<<2];
int n;
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {int x=0,f=1; char c=nc(); while(c<48) {if(c=='-') f=-1; c=nc();} while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x*f;}
int qpow(int x,int y)
{
int ans=1;
while(y)
{
if(y&1) ans=(ll)ans*x%mod;
y>>=1;
x=(ll)x*x%mod;
}
return ans;
}
void init()
{
fac[0]=1; for(int i=1;i<=1000;i++) fac[i]=(ll)fac[i-1]*i%mod;
inv[0]=1; for(int i=1;i<=1000;i++) inv[i]=qpow(fac[i],mod-2);
}
void ntt(int *a,int len,int flg)
{
int i,j,k,t,w,x,tmp;
for(i=k=0;i<len;i++)
{
if(i>k) swap(a[i],a[k]);
for(j=len>>1;(k^=j)<j;j>>=1);
}
for(k=2;k<=len;k<<=1)
{
t=k>>1;
x=qpow(3,(mod-1)/k);
if(flg==-1) x=qpow(x,mod-2);
for(i=0;i<len;i+=k)
for(j=i,w=1;j<i+t;j++)
{
tmp=(ll)a[j+t]*w%mod;
a[j+t]=(a[j]-tmp+mod)%mod;
a[j]=(a[j]+tmp)%mod;
w=(ll)w*x%mod;
}
}
if(flg==-1) for(t=qpow(len,mod-2),i=0;i<len;i++) a[i]=(ll)a[i]*t%mod;
}
int calc(int k)
{
int l=1;
while(l<=max((num[4]-k)<<1,(n-4*k)<<1)) l<<=1;
while(l<=(num[4]<<2)) l<<=1;
for(int i=1;i<=4;i++)
{
len[i]=num[i]-k;
for(int j=0;j<=l;j++) b[i][j]=0;
}
for(int i=1;i<=4;i++) for(int j=0;j<=len[i];j++) b[i][j]=inv[j];
for(int i=1;i<=4;i++) ntt(b[i],l,1);
for(int i=2;i<=4;i++) for(int j=0;j<l;j++) b[1][j]=(ll)b[1][j]*b[i][j]%mod;
ntt(b[1],l,-1);
return (ll)b[1][n-4*k]*fac[n-4*k]%mod;
}
int main()
{
n=rd(); for(int i=1;i<=4;i++) num[i]=rd();
for(int i=0;i<=n;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
sort(num+1,num+5);
int l=1;
while(l<=(num[4]<<2)) l<<=1;
init();
int ans=0;
for(int i=0;(i<<2)<=min(n,num[1]<<2);i++)
{
int mdl=(ll)calc(i)*C[n-3*i][i]%mod;
if(i&1) (ans-=mdl)%=mod;
else (ans+=mdl)%=mod;
}
printf("%d\n",(ans+mod)%mod);
return 0;
}

最新文章

  1. 【转】GitHub 排名前 100 的安卓、iOS项目简介
  2. ios中自定义tableView,CollectionView的cell什么时候用nib加载,什么时候用标识重用
  3. 从零开始一个iOS项目(一)——基本准备以及cocopods的安装
  4. Daily Scrum 11.3
  5. 完美实现开机启动虚拟WIFI,顺便实现目前的WP8系统使用VPN(7.1修)
  6. Oracle存储过程中传入参数,传出字符串
  7. 【leetcode】clone-graph
  8. Lintcode: Product of Array Exclude Itself
  9. 有关获取session属性时报nullPointException(空指针异常)的解决方案
  10. OC1_点语法
  11. Java获得UTC时间
  12. python —print
  13. 读Zepto源码之Deferred模块
  14. Python中的单例模式
  15. Poj2723:Get Luffy Out
  16. ELK+filebeat、kafka、zookeeper搭建文档
  17. [模板][Luogu3387] 缩点 - Tarjan, 拓扑+DP
  18. JavaScript数据结构与算法(二) 队列的实现
  19. CentOS-7修改主机名
  20. NOIP2018初赛游记

热门文章

  1. WORD与DWORD
  2. Swift 编程思想 Part 4:map all the things!
  3. 接口和类方法中的 SELF
  4. [POJ] 2411 Mondriaan's Dream
  5. [LUOGU] P1880 [NOI1995]石子合并
  6. day20-python之装饰器
  7. 文件的软硬链接&amp; 文件编辑vi和vim
  8. js 类型之间的相互转化
  9. errno的定义
  10. [转]ARM平台下独占访问指令LDREX和STREX