正题

题目链接:https://www.luogu.com.cn/problem/CF960G


题目大意

求有多少个长度为\(n\)的排列,使得有\(A\)个前缀最大值和\(B\)个后缀最大值。

\(0\leq n,A,B\leq 10^5\)


解题思路

显然的是把最大的数两边然后左边的是前缀最大值,右边的是前缀最小值。

然后考虑两个前缀最大值之间其实可以插任何数字,但是最大的一定要排在前面。

其实就是这些数字分成若干个圆排列的个数,就是第一类斯特林数。

枚举左右两边的数量就有

\[\sum_{i=0}^{n-1}\begin{bmatrix}i\\a-1\end{bmatrix}\begin{bmatrix}n-i-1\\b-1\end{bmatrix}\binom{n-1}{i}
\]

然后组合意义理解一下,我们可以考虑直接分成\(a+b-2\)个环然后再依次排列到左右就是

\[\begin{bmatrix}n-1\\a+b-2\end{bmatrix}\binom{a+b-2}{a-1}
\]

这个看起来就好做很多,先考虑怎么求第一类斯特林数。

考虑递推式

\[\begin{bmatrix}n\\m\end{bmatrix}=\begin{bmatrix}n-1\\m-1\end{bmatrix}+\begin{bmatrix}n-1\\m\end{bmatrix}\times(n-1)
\]

可以理解为\(0\sim n-1\)个里面选出\(m\)个数的乘积之和。

用生成函数做就是

\[\prod_{i=0}^{n-1}(x+i)
\]

用分治+\(NTT\)算就好了,当然推式子还有更快的方法

时间复杂度\(O(n\log^2 n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=4e5+10,P=998244353;
struct Poly{
ll f[N];ll n;
}F[20];
ll n,a,b,f[N],g[N],r[N];bool use[20];
ll power(ll x,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*x%P;
x=x*x%P;b>>=1;
}
return ans;
}
void NTT(ll *f,ll n,ll op){
for(ll i=0;i<n;i++)
if(i<r[i])swap(f[i],f[r[i]]);
for(ll p=2;p<=n;p<<=1){
ll tmp=power(3,(P-1)/p),len=p>>1;
if(op==-1)tmp=power(tmp,P-2);
for(ll k=0;k<n;k+=p){
ll buf=1;
for(ll i=k;i<k+len;i++){
ll tt=f[i+len]*buf%P;
f[i+len]=(f[i]-tt+P)%P;
f[i]=(f[i]+tt)%P;
buf=buf*tmp%P;
}
}
}
if(op==-1){
ll invn=power(n,P-2);
for(ll i=0;i<n;i++)
f[i]=f[i]*invn%P;
}
return;
}
void mul(Poly &x,Poly &y){
ll n=1;while(n<x.n+y.n)n<<=1;
for(ll i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)?(n>>1):0);
for(ll i=0;i<n;i++)f[i]=x.f[i],g[i]=y.f[i];
NTT(f,n,1);NTT(g,n,1);
for(ll i=0;i<n;i++)f[i]=f[i]*g[i]%P;
NTT(f,n,-1);
for(ll i=0;i<n;i++)x.f[i]=f[i],y.f[i]=0;
x.n=x.n+y.n-1;return;
}
ll FindE(){
for(ll i=0;i<20;i++)
if(!use[i])return i;
}
ll solve(ll l,ll r){
if(l==r){
ll p=FindE();
F[p].f[0]=l;F[p].f[1]=1;
F[p].n=2;use[p]=1;return p;
}
ll mid=(l+r)>>1;
ll ls=solve(l,mid),rs=solve(mid+1,r);
mul(F[ls],F[rs]);use[rs]=0;return ls;
}
ll C(ll n,ll m){
ll ans=1,fac=1;
for(ll i=m+1;i<=n;i++)ans=ans*i%P;
for(ll i=1;i<=n-m;i++)fac=fac*i%P;
return ans*power(fac,P-2)%P;
}
signed main()
{
scanf("%lld%lld%lld",&n,&a,&b);
if(!a||!b||a+b-2>n-1)return puts("0")&0;
if(n==1)return puts("1")&0;
ll p=solve(0,n-2);
printf("%lld\n",F[p].f[a+b-2]*C(a+b-2,a-1)%P);
return 0;
}

CF960G-Bandit Blues【第一类斯特林数,分治,NTT】的更多相关文章

  1. CF960G Bandit Blues 第一类斯特林数、NTT、分治/倍增

    传送门 弱化版:FJOI2016 建筑师 由上面一题得到我们需要求的是\(\begin{bmatrix} N - 1 \\ A + B - 2 \end{bmatrix} \times \binom ...

  2. [CF960G]Bandit Blues(第一类斯特林数+分治卷积)

    Solution: ​ 先考虑前缀,设 \(f(i, j)\) 为长度为 \(i\) 的排列中满足前缀最大值为自己的数有 \(j\) 个的排列数. 假设新加一个数 \(i+1\) 那么会有: \[ f ...

  3. CF960G Bandit Blues 第一类斯特林数+分治+FFT

    题目传送门 https://codeforces.com/contest/960/problem/G 题解 首先整个排列的最大值一定是 \(A\) 个前缀最大值的最后一个,也是 \(B\) 个后缀最大 ...

  4. CF960G Bandit Blues 【第一类斯特林数 + 分治NTT】

    题目链接 CF960G 题解 同FJOI2016只不过数据范围变大了 考虑如何预处理第一类斯特林数 性质 \[x^{\overline{n}} = \sum\limits_{i = 0}^{n}\be ...

  5. Codeforces960G Bandit Blues 【斯特林数】【FFT】

    题目大意: 求满足比之前的任何数小的有A个,比之后的任何数小的有B个的长度为n的排列个数. 题目分析: 首先写出递推式,设s(n,k)表示长度为n的排列,比之前的数小的数有k个. 我们假设新加入的数为 ...

  6. CF960G Bandit Blues 分治+NTT(第一类斯特林数)

    $ \color{#0066ff}{ 题目描述 }$ 给你三个正整数 \(n\),\(a\),\(b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大 ...

  7. 【CF960G】Bandit Blues(第一类斯特林数,FFT)

    [CF960G]Bandit Blues(第一类斯特林数,FFT) 题面 洛谷 CF 求前缀最大值有\(a\)个,后缀最大值有\(b\)个的长度为\(n\)的排列个数. 题解 完完全全就是[FJOI] ...

  8. CF960G Bandit Blues(第一类斯特林数)

    传送门 可以去看看litble巨巨关于第一类斯特林数的总结 设\(f(i,j)\)为\(i\)个数的排列中有\(j\)个数是前缀最大数的方案数,枚举最小的数的位置,则有递推式\(f(i,j)=f(i- ...

  9. 【cf960G】G. Bandit Blues(第一类斯特林数)

    传送门 题意: 现在有一个人分别从\(1,n\)两点出发,包中有一个物品价值一开始为\(0\),每遇到一个价值比包中物品高的就交换两个物品. 现在已知这个人从左边出发交换了\(a\)次,从右边出发交换 ...

随机推荐

  1. node 报错 throw er; // Unhandled 'error' event 解决办法

    node 报错 Starting child process with 'node web.js' events.js:183 throw er; // Unhandled 'error' event ...

  2. JSONObject和JSONArray的使用

    1.Json里面的数据是以一种键值对的方式存在("key","value"),其语法多是{},[]的单独形式或者组合形式. 2.对于JsonObject: a. ...

  3. Map 综述(二):彻头彻尾理解 LinkedHashMap

    摘要: HashMap和双向链表合二为一即是LinkedHashMap.所谓LinkedHashMap,其落脚点在HashMap,因此更准确地说,它是一个将所有Entry节点链入一个双向链表的Hash ...

  4. 用tinyxml2读写xml文件_C++实现

    下载源代码 开源代码github地址: https://github.com/leethomason/tinyxml2 添加工程文件 将源代码目录中 tinyxml2.h 和 tinyxml2.cpp ...

  5. 小程序使用 lodash 的问题

    import _ from 'lodash' 报错: vendor.js:11874 Uncaught TypeError: Cannot read property 'prototype' of u ...

  6. vue 手机号码隐藏中间

    template {{ phone | mobileFilter }}   data export default { filters:{         mobileFilter(val){     ...

  7. 人生重开模拟器「GitHub 热点速览 v.21.36」

    作者:HelloGitHub-小鱼干 人生是不能重来的,但是 lifeRestart 能满足你的重开心愿.初始值不满意,你可以一直随机生成或者自动添加颜值.智力.运气值,倒是一种"重生&qu ...

  8. 查看所有日志命令:journalctl

    journalctl命令作用:实时查看所有日志(内核日志和应用日志) 语法格式: journalctl [参数] 常用参数:-k 查看内核日志-b 查看系统本次启动的日志-u 查看指定服务的日志-n ...

  9. C#中的文本到语音

    本演示说明了如何使用c#.net Windows Forms应用程序中的system.speech库将文本转换为语音.Microsoft .NET框架提供System.Speech.Synthesis ...

  10. try/catch捕获处理异常

    1.throws是中断处理,后续代码不能执行 try/catch方法体之后的后续代码有没有异常都可以继续执行: 2.当try方法体中出现异常才会执行catch方法体中代码