题意

找有多少个长度为n的排列,使得从左往右数,有a个元素比之前的所有数字都大,从右往左数,有b个元素比之后的所有数字都大。

n<=2*10^5,a,b<=n

输入格式

输入三个整数n,a,b。

输出格式

输出一个整数,表示答案。

思路

这道题是真的神啊...

首先,根据官方题解的思路,首先有一个n^2的DP:

定义dp[i][j]表示一个长度为i的排列,从前往后数一共有j个数字大于所有排在它前面的数字。

首先有转移式:

\[dp[i][j]=dp[i-1][j-1]+(i-1)*dp[i-1][j]
\]

怎么理解这个式子呢?

首先,最后的排列一定是这样一个形式:



中间那个是最大值(n),那么前面第一个位置到第二个位置之间不能放任意一个数;第二个位置与第三个位置之间能够放1~ 4之间的数;第三个与第四个之间能够放4~9...我们能够发现,相邻两个位置能够放的数一定是小于前一个位置的。那么我们就可以根据选中的关键点(如上图中的1,4,9)将前半部分的序列分为几部分,每一部分的代表元素为这一部分中的数字的最大值。

例如:1-423-95678,就可以看成是3个部分。代表元素分别是1,4,9。

根据定义,现在考虑的是一共有i个数字,分成了j段。考虑加入一个新的最小的数字,考虑它放在哪里:

  1. 放在开头,自己成为一个新的部分,就由dp[i-1][j-1]转移而来。

    2.因为是最小的,所以可以放在之前的所有已经存在的部分中,那么有(i-1)中方案,就由(i-1)*dp[i-1][j]转移而来。

这样子就有了dp的转移式,很显然最后的答案就是:

\[Ans=\sum_{i=1}^{n}(dp[i][a-1]*dp[n-i-1][b-1])*C_{n-1}^{i-1}
\]

其实就是枚举最大值的位值i,然后从剩下的n-1中选出i-1个,再将这i-1个数字分为a-1个部分,后面的n-i-1个位置分为b-1个部分。那就有\(O(n^2)\)的算法了。


有了上述的式子之后,接下来就比较好处理了。

仔细观察dp的转移式,我们会惊奇的发现它竟然和第一类斯特林数的递推式是一样的。也就是:

\[dp[i][j]=[^{i}_{j}]
\]

第一类斯特林数是将i个数分成j个圆排列的方案数(忽略顺序的前提下)。而我们可以把之前定义的"部分"每一个都看成是一个圆排列,每一个都把其中最大的值通过圆排列转到这一部分开头的位置,就完美的对应上了。

而在全局看,我们可以先将n-1个数字分配当a+b-2个圆排列里面去,然后再将这a+b-2分成左边a-1个和右边b-1个,就是简单组合数了。那么答案式就可以进一步化简为:

\[Ans=[_{a+b-2}^{\ \ n-1}]*C_{a+b-2}^{a-1}
\]

这样子我们的主要问题就变为求前面那个斯特林数就可以了。


而如何快速求斯特林数又是另外一个问题了...

感觉这里写一遍的话,好像有些冗长了。就在下面贴了一个链接,在里面我会尽可能详细的讲解如何快速求解S(n,k)。

如何快速求解第一类斯特林数

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 600000
#define MO 998244353
#define G 3
using namespace std;
int seq[MAXN+5];
int n,a,b;
int fact[MAXN+5],inv[MAXN+5];
int PowMod(int x,int y)
{
int ret=1;
while(y)
{
if(y&1)
ret=1LL*ret*x%MO;
x=1LL*x*x%MO;
y>>=1;
}
return ret;
}
void Prepare()
{
fact[0]=1;
for(int i=1;i<=MAXN;i++)
fact[i]=1LL*fact[i-1]*i%MO;
inv[MAXN]=PowMod(fact[MAXN],MO-2);
for(int i=MAXN-1;i>=0;i--)
inv[i]=1LL*inv[i+1]*(1LL*i+1LL)%MO;
}
void Reverse(int A[],int deg)
{
for(int i=0;i<deg/2;i++)
swap(A[i],A[deg-i-1]);
}
void NTT(int P[],int len,int oper)
{
for(int i=1,j=0;i<len-1;i++)
{
for(int s=len;j^=s>>=1,~j&s;);
if(i<j) swap(P[i],P[j]);
}
int unit,unit_p0;
for(int d=0;(1<<d)<len;d++)
{
int m=(1<<d),m2=m*2;
unit_p0=PowMod(G,(MO-1)/m2);
if(oper==-1)
unit_p0=PowMod(unit_p0,MO-2);
for(int i=0;i<len;i+=m2)
{
unit=1;
for(int j=0;j<m;j++)
{
int &P1=P[i+j+m],&P2=P[i+j];
int t=1LL*unit*P1%MO;
P1=((1LL*P2-1LL*t)%MO+MO)%MO;
P2=(1LL*P2+1LL*t)%MO;
unit=1LL*unit*unit_p0%MO;
}
}
}
if(oper==-1)
{
int inv=PowMod(len,MO-2);
for(int i=0;i<len;i++)
P[i]=1LL*P[i]*inv%MO;
}
}
void Mul(int ret[],int _x[],int l1,int _y[],int l2)
{
static int RET[MAXN+5],X[MAXN+5],Y[MAXN+5];
int len=1;
while(len<l1+l2) len<<=1;
copy(_x,_x+l1,X);copy(_y,_y+l2,Y);
fill(X+l1,X+len,0);fill(Y+l2,Y+len,0);
NTT(X,len,1);NTT(Y,len,1);
for(int i=0;i<len;i++)
RET[i]=1LL*X[i]*Y[i]%MO;
NTT(RET,len,-1);
copy(RET,RET+l1+l2,ret);
}
void Get(int deg,int A[],int B[])
{
static int tmpA[MAXN+5],tmpB[MAXN+5];
int len=deg/2;
for(int i=0;i<len+1;i++)
tmpA[i]=1LL*PowMod(len,i)*inv[i]%MO;
fill(tmpA+len+1,tmpA+deg+1,0);
for(int i=0;i<len+1;i++)
tmpB[i]=1LL*fact[i]*A[i]%MO;
fill(tmpB+len+1,tmpB+deg+1,0);
Reverse(tmpA,len+1);
Mul(tmpA,tmpA,len+1,tmpB,len+1);
for(int i=0;i<=len;i++)
tmpA[i]=1LL*tmpA[i+len]*inv[i]%MO;
copy(tmpA,tmpA+len+1,B);
}
void Solve(int deg,int B[])
{
static int tmpB[MAXN+5];
if(deg==1)
{
B[1]=1;
return;
}
Solve(deg/2,B);
int hf=deg/2;
copy(B,B+hf+1,tmpB);
fill(tmpB+hf+1,tmpB+deg+1,0);
Get(deg-deg%2,tmpB,tmpB+hf+1);
Mul(B,tmpB,hf+1,tmpB+hf+1,hf+1);
if(deg%2==1)
for(int i=deg;i>=1;i--)
B[i]=(1LL*B[i]*(1LL*deg-1LL)%MO+1LL*B[i-1])%MO;
}
int C(int x,int y)
{
return 1LL*fact[x]*inv[y]%MO*inv[x-y]%MO;
}
int main()
{
Prepare();
scanf("%d %d %d",&n,&a,&b);
if(n==1&&a==1&&b==1)
//注意加特判,也可以通过改变Solve底层的返回条件来兼容这种情况
printf("1\n");
else if((n>1&&a==1&&b==1)||a==0||b==0)
printf("0\n");
else
{
Solve(n-1,seq);//快速求第一类斯特林数(nlogn)
int part1=seq[a+b-2];
int ans=1LL*C(a+b-2,a-1)*part1%MO;
printf("%d\n",ans);
}
return 0;
}
/*
2 2 1 5 2 2 */

【2019雅礼集训】【CF 960G】【第一类斯特林数】【NTT&多项式】permutation的更多相关文章

  1. 【2019雅礼集训】【最大费用流】【模型转换】D2T3 sum

    目录 题意 输入格式 输出格式 思路 代码 题意 现在你有一个集合{1,2,3,...,n},要求你从中取出一些元素,使得这些元素两两互质.问你能够取出的元素总和最多是多少? 输入格式 一个整数n 输 ...

  2. 【2019雅礼集训】【可持久化线段树】【模型转化】D1T2Permutation

    目录 题意 输入格式 输出格式 思路 代码 题意 给定一个长度为n的序列A[],你需要确定一个长度为n的排列P[],定义当前排列的值为: \[\sum_{i=1}^{n}{A[i]P[i]}\] 现在 ...

  3. 如何快速求解第一类斯特林数--nlog^2n + nlogn

    目录 参考资料 前言 暴力 nlog^2n的做法 nlogn的做法 代码 参考资料 百度百科 斯特林数 学习笔记-by zhouzhendong 前言 首先是因为这道题,才去研究了这个玩意:[2019 ...

  4. 雅礼集训2019 D7T2 Subsequence

    雅礼集训2019 D7T2 Subsequence 直接贴题解: 平衡树代码: #include<bits/stdc++.h> #define ll long long #define N ...

  5. 雅礼集训1-9day爆零记

    雅礼集训1-9day爆零记 先膜一下虐爆我的JEFF巨佬 Day0 我也不知道我要去干嘛,就不想搞文化科 (文化太辣鸡了.jpg) 听李总说可以去看(羡慕)各路大佬谈笑风声,我就报一个名吧,没想到还真 ...

  6. LOJ_6045_「雅礼集训 2017 Day8」价 _最小割

    LOJ_6045_「雅礼集训 2017 Day8」价 _最小割 描述: 有$n$种减肥药,$n$种药材,每种减肥药有一些对应的药材和一个收益. 假设选择吃下$K$种减肥药,那么需要这$K$种减肥药包含 ...

  7. 雅礼集训【Day6-1】字符串

    雅礼集训[Day6-1]字符串 假设我们有串\(a\),我们设\(a'\)为\(a\)翻转后按为取反过后的串. 我们只考虑前一半的,长为\(m\)的串.如果前半截匹配了\(a\)或者\(a'\),则\ ...

  8. 「雅礼集训 2017 Day7」事情的相似度

    「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...

  9. 「雅礼集训 2017 Day2」解题报告

    「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...

随机推荐

  1. ORM系列之Entity FrameWork详解

    一. 谈情怀 从第一次接触开发到现在(2018年),大约有六年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQ ...

  2. [C++]UVaLive7324 ASCII Addtion

    Description Nowadays, there are smartphone applications that instantly translate text and even solve ...

  3. CORS跨域 Ajax headers 问题

    今天我们遇到了一个CORS跨域的问题Ajax如下 var url = "http://localhost:11980/api/Demo/GetString"; //api地址 $. ...

  4. Jmeter、Postman 、 loadrunner SoapUI 接口测试工具

    一. loadrunner  简称 LR 二. Jmeter 1.安装包:apache-jmeter-4.0.tgz   解压.学会此工具的使用  和POSTman 一样的. 2.本机测试:双击apa ...

  5. 如何安装zsh,终极shell

    1,教程 教程 上知乎直接搜索终极shell也可以,话不多说,直接自己看教程吧 2,总之就是 sudo apt-get install zsh chsh ...修改设置 wget下载oh-my-zsh ...

  6. C++变量/函数命名规范

    ## 参照Google C++编程规范之变量命名 1. 变量 变量名一律小写,单词间以下划线相连.类的成员变量以下划线结尾. 普通变量命名 举例: string window_name; // OK ...

  7. 论文学习笔记--无缺陷样本产品表面缺陷检测 A Surface Defect Detection Method Based on Positive Samples

    文章下载地址:A Surface Defect Detection Method Based on Positive Samples 第一部分  论文中文翻译 摘要:基于机器视觉的表面缺陷检测和分类可 ...

  8. Spring Cloud 2-Bus 消息总线(九)

    Spring Cloud  Bus  1.服务端配置 pom.xml application.yml 2.客户端配置 pom.xml application.yml Controller.java 3 ...

  9. 帆软报表(finereport)决策平台笔记(持续更新)

    1,报表发布的流程制作报表>>>报表部署>>>WEB页面集成>>>权限配置 2,报表部署到Tomcat的简单介绍A,独立部署将WebReport文 ...

  10. java程序性能分析之thread dump和heap dump

    一.dump基本概念 在故障定位(尤其是out of memory)和性能分析的时候,经常会用到一些文件来帮助我们排除代码问题.这些文件记录了JVM运行期间的内存占用.线程执行等情况,这就是我们常说的 ...