$ \color{#0066ff}{ 题目描述 }$

给你三个正整数 \(n\),\(a\),\(b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大值的数的个数,求长度为 \(n\) 的排列中满足 \(A = a\) 且 \(B = b\) 的排列个数。\(n \le 10^5\),答案对 \(998244353\) 取模。

\(\color{#0066ff}{输入格式}\)

三个整数n,a,b

\(\color{#0066ff}{输出格式}\)

方案数

\(\color{#0066ff}{输入样例}\)

1 1 1

2 1 1

2 2 1

5 2 2

\(\color{#0066ff}{输出样例}\)

1

0

1

22

\(\color{#0066ff}{数据范围与提示}\)

\(N\) ( $1<=N<=10^{5} $ ), $A $and $ B$ ( $0<=A,B<=N $).

\(\color{#0066ff}{题解}\)

显然当\(a+b-1>n\)时,无解

考虑DP, \(f[i][j]\)表示i的排列有j个前缀最大值的方案数

考虑枚举1的位置\(f[i][j] = f[i-1][j-1]+(i-1)*f[i-1][j]\)

这是第一类斯特林数

实际上第一维滚动之后,可以发现, 就是把整个数组移动一位再加上自己的值*dp轮数

就相当于第i轮有i次操作,有1的方案取一个球,有i-1的方案一个球不取

于是构造生成函数\(\begin{aligned}\prod_{i=0}^{n-2}(x+i)\end{aligned}\)

本来应该是n-1的,实际上n的方案从1只转移n-1次, 所以-1

这个式子可以分治+NTT快速求出

最后还要组合一下,可以考虑每个产生贡献的值,a个和b个分别分成a-1和b-1段

最后再乘一个\(C_{a+b-2}^{a-1}\)即可

#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 4e5 + 10;
const int mod = 998244353;
int len, r[maxn];
using std::vector;
LL ksm(LL x, LL y) {
LL re = 1LL;
while(y) {
if(y & 1) re = re * x % mod;
x = x * x % mod;
y >>= 1;
}
return re;
}
void FNTT(vector<int> &A, int flag) {
A.resize(len);
for(int i = 0; i < len; i++) if(i < r[i]) std::swap(A[i], A[r[i]]);
for(int l = 1; l < len; l <<= 1) {
int w0 = ksm(3, (mod - 1) / (l << 1));
for(int i = 0; i < len; i += (l << 1)) {
int w = 1, a0 = i, a1 = i + l;
for(int k = 0; k < l; k++, a0++, a1++, w = 1LL * w0 * w % mod) {
int tmp = 1LL * A[a1] * w % mod;
A[a1] = ((A[a0] - tmp) % mod + mod) % mod;
A[a0] = (A[a0] + tmp) % mod;
}
}
}
if(!(~flag)) {
std::reverse(A.begin() + 1, A.end());
int inv = ksm(len, mod - 2);
for(int i = 0; i < len; i++) A[i] = 1LL * A[i] * inv % mod;
}
}
vector<int> operator * (vector<int> A, vector<int> B) {
int tot = A.size() + B.size() - 1;
for(len = 1; len <= tot; len <<= 1);
for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
FNTT(A, 1), FNTT(B, 1);
std::vector<int> ans;
for(int i = 0; i < len; i++) ans.push_back(1LL * A[i] * B[i] % mod);
FNTT(ans, -1);
ans.resize(tot);
return ans;
}
int n, a, b;
std::vector<int> work(int l, int r) {
vector<int> ans;
if(l == r) {
ans.resize(2, 0);
ans[1] += 1, ans[0] += l;
return ans;
}
int mid = (l + r) >> 1;
return work(l, mid) * work(mid + 1, r);
}
LL C(int x, int y) {
LL ans1 = 1, ans2 = 1;
for(int i = y + 1; i <= x; i++) ans1 = 1LL * ans1 * i % mod;
for(int i = 1; i <= x - y; i++) ans2 = 1LL * ans2 * i % mod;
return 1LL * ans1 * ksm(ans2, mod - 2) % mod;
}
int main() {
n = in(), a = in(), b = in();
if(!a || !b || a + b - 1 > n) return puts("0"), 0;
if(n == 1) return puts("1"), 0;
std::vector<int> ans;
ans = work(0, n - 2);
printf("%lld\n", 1LL * ans[a + b - 2] * C(a + b - 2, a - 1) % mod);
return 0;
}

CF960G Bandit Blues 分治+NTT(第一类斯特林数)的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. CF960G-Bandit Blues【第一类斯特林数,分治,NTT】

    正题 题目链接:https://www.luogu.com.cn/problem/CF960G 题目大意 求有多少个长度为\(n\)的排列,使得有\(A\)个前缀最大值和\(B\)个后缀最大值. \( ...

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

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

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

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

  9. CF960G(第一类斯特林数)

    题目 CF960G 做法 设\(f(i,j)\)为\(i\)个数的序列,有\(j\)个前缀最大值的方案数 我们考虑每次添一个最小数,则有:\(f(i,j)=f(i-1,j)+(i-1)*f(i-1,j ...

随机推荐

  1. Spring学习笔记(三)--Convert System设计

    一.概述 转换系统是不同范式系统间交互常常会需要碰到的,如WEB 中HTTP KV文本流到对象的转换和对象到http KV文本流的转换:还有对象到关系型DB和关系型DB到对象之间的转换(ORM). 一 ...

  2. PHP异常处理详解

      PHP异常处理详解 异常处理(又称为错误处理)功能提供了处理程序运行时出现的错误或异常情况的方法. 异常处理通常是防止未知错误产生所采取的处理措施.异常处理的好处是你不用再绞尽脑汁去考虑各种错误, ...

  3. 71. Simplify Path压缩文件的绝对路径

    [抄题]: Given an absolute path for a file (Unix-style), simplify it. For example,path = "/home/&q ...

  4. hadoop组件启动和关闭命令

    一.启动相关组件之前 一般安装完hadoop之后需要格式化一遍hdfs: hdfs namenode -format 然后再进行其他组件的启动,hadoop相关组件都是用位于...hadoop/sbi ...

  5. ansible playbook模式及语法

    一.什么是playbook及其组成 什么是playbook playbook 翻译过来就是"剧本" playbook的组成 play:定义的是主机的角色 task:定义的是具体执行 ...

  6. Java 8特性

    1. Java8的新特性 1.1. Lambda表达式和函数式接口 最简单的Lambda表达式可以用逗号分隔的参数列表.->符号和功能语句块来表示.示例如下: Arrays.asList( &q ...

  7. c/c++笔试面试试题

    C 试题(纯属转载) 1.求下面函数的返回值(微软) int func(x) {     int countx = 0;     while(x)     {           countx ++; ...

  8. AngularJs(v1)相关知识和经验的碎片化记录

    1.利用angular指令监听ng-repeat渲染完成后执行脚本 http://www.cnblogs.com/wangmeijian/p/5141266.html 2.$http的POST请求中请 ...

  9. Sublime Text 2 安装配置插件

    最近学习python,看网上推荐用sublime text2挺方便,就学习了一下对sublime text2 安装插件,先放在这里,以备以后查找 根据晚上资料修改,原文请看这里 Python的自动补全 ...

  10. aspnetcore的那些actionresult们

    比MVC5多了n个actionresult,傻傻分不清,整理了下,妈妈再也不用担心了 https://docs.asp.net/projects/api/en/latest/autoapi/Micro ...