$ \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. 对图片进行X,Y轴的翻转,黑白色转变

    做完了修改图片的程序.用的Winfrom做的.昨天基本完成,记录一下使用的东西. 首先是将需要加载的图片放入picturebox控件,这里用到了openfiledialog 打开文件对话框 strin ...

  2. Result Grouping / Field Collapsing-结果分组

    WiKi:http://wiki.apache.org/solr/FieldCollapsing Introduction 字段折叠和结果分组是考虑相同solr功能的两种不同的方式. 字段折叠折叠一组 ...

  3. Python Windows下打包成exe文件

    Python Windows 下打包成exe文件,使用PyInstaller 软件环境: 1.OS:Win10 64 位 2.Python 3.7 3.安装PyInstaller 先检查是否已安装Py ...

  4. spring mabatis springmvc 看过

    .Spring中AOP的应用场景.Aop原理.好处? 答:AOP--Aspect Oriented Programming面向切面编程:用来封装横切关注点,具体可以在下面的场景中使用: Authent ...

  5. 第二话:javascript中闭包的理解

    闭包是什么? 通过闭包,子函数得以访问父函数的上下文环境,即使父函数已经结束执行. OK,我来简单叙述下,先上图. 都知道函数是javascript整个世界,对象是函数,方法是函数,并且js中实质性的 ...

  6. python3--json序列化

    # Auther: Aaron Fan # 把数据存入到一个文件中 # json格式的数据几乎可以通用语任何编程语言,但是仅仅只是简单的格式转换# 比如:字典.列表.元组.字符串这些,像函数.类就不可 ...

  7. [SoapUI] 比较两个不同环境下XML格式的Response, 结果不同时设置Test Step的执行状态为失败

    import org.custommonkey.xmlunit.* def responseTP=context.expand( '${Intraday Table_TP#Response}' ) d ...

  8. ES5、6、7浅析

    ECMA Script 它是一种由ECMA组织(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范 而我们学的JavaScript是ECMA的实现, 但术语ECMAScript和JavaScript ...

  9. 视觉SLAM实战(一):RGB-D SLAM V2

    写在前面 首先打个广告.SLAM研究者交流QQ群:254787961.欢迎各路大神和小白前来交流. 看了前面三篇博文之后,是不是有同学要问:博主你扯了那么多有用没用的东西,能不能再给力一点,拿出一个我 ...

  10. C# Winform 跨线程更新UI控件常用方法总结(转)

    出处:http://www.tuicool.com/articles/FNzURb 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件 ...