CF960G Bandit Blues 分治+NTT(第一类斯特林数)
$ \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(第一类斯特林数)的更多相关文章
- CF960G Bandit Blues 【第一类斯特林数 + 分治NTT】
题目链接 CF960G 题解 同FJOI2016只不过数据范围变大了 考虑如何预处理第一类斯特林数 性质 \[x^{\overline{n}} = \sum\limits_{i = 0}^{n}\be ...
- CF960G Bandit Blues 第一类斯特林数、NTT、分治/倍增
传送门 弱化版:FJOI2016 建筑师 由上面一题得到我们需要求的是\(\begin{bmatrix} N - 1 \\ A + B - 2 \end{bmatrix} \times \binom ...
- [CF960G]Bandit Blues(第一类斯特林数+分治卷积)
Solution: 先考虑前缀,设 \(f(i, j)\) 为长度为 \(i\) 的排列中满足前缀最大值为自己的数有 \(j\) 个的排列数. 假设新加一个数 \(i+1\) 那么会有: \[ f ...
- CF960G Bandit Blues 第一类斯特林数+分治+FFT
题目传送门 https://codeforces.com/contest/960/problem/G 题解 首先整个排列的最大值一定是 \(A\) 个前缀最大值的最后一个,也是 \(B\) 个后缀最大 ...
- 【CF960G】Bandit Blues(第一类斯特林数,FFT)
[CF960G]Bandit Blues(第一类斯特林数,FFT) 题面 洛谷 CF 求前缀最大值有\(a\)个,后缀最大值有\(b\)个的长度为\(n\)的排列个数. 题解 完完全全就是[FJOI] ...
- CF960G-Bandit Blues【第一类斯特林数,分治,NTT】
正题 题目链接:https://www.luogu.com.cn/problem/CF960G 题目大意 求有多少个长度为\(n\)的排列,使得有\(A\)个前缀最大值和\(B\)个后缀最大值. \( ...
- CF960G Bandit Blues(第一类斯特林数)
传送门 可以去看看litble巨巨关于第一类斯特林数的总结 设\(f(i,j)\)为\(i\)个数的排列中有\(j\)个数是前缀最大数的方案数,枚举最小的数的位置,则有递推式\(f(i,j)=f(i- ...
- 【cf960G】G. Bandit Blues(第一类斯特林数)
传送门 题意: 现在有一个人分别从\(1,n\)两点出发,包中有一个物品价值一开始为\(0\),每遇到一个价值比包中物品高的就交换两个物品. 现在已知这个人从左边出发交换了\(a\)次,从右边出发交换 ...
- CF960G(第一类斯特林数)
题目 CF960G 做法 设\(f(i,j)\)为\(i\)个数的序列,有\(j\)个前缀最大值的方案数 我们考虑每次添一个最小数,则有:\(f(i,j)=f(i-1,j)+(i-1)*f(i-1,j ...
随机推荐
- ansible介绍与安装
一.什么是ansible ansible是python中一套模块,系统中的一套自动化工具,可以用来作系统管理.自动化命令等任务. 二.ansible优势 .ansible是Python中一套完整的自动 ...
- 粗略了解fill与fill_n
以前只知道数组赋值时用memset(): 而这几天却了解到了一个函数:fill(); 感觉以后会有用吧... std::fill template <class ForwardIterator, ...
- c linux ping 实现
摘自:https://blog.csdn.net/weibo1230123/article/details/79891018 ping的实现和代码分析一.介绍 ping命令是用来查看网络上另一 ...
- 白盒测试实践项目(day4)
华中科技大学教材订购系统 代码评审会议纪要 与会人员: 胡俊辉.杨瑞丰.汪鸿.张颖.李建文 评审标准: 此次代码评审会议,我们小组选用了阿里巴巴Java开发手册对代码进行评审. 会议过程: 1:周末下 ...
- mac安装mysql及终端操作mysql与pycharm的数据库可视化
一.Mac安装mysql 首先下载mysql,地址:https://dev.mysql.com/downloads/mysql/ 然后已知安装就好了,会出现让你记住密码的提示,然后就安装好了.... ...
- C#通过Redis实现分布式锁
Redis有三个最基本属性来保证分布式锁的有效实现: 安全性: 互斥,在任何时候,只有一个客户端能持有锁. 活跃性A:没有死锁,即使客户端在持有锁的时候崩溃,最后也会有其他客户端能获得锁,超时机制. ...
- 优秀前端工程师必备: (总结) 清除原生ios按钮样式
写移动端的web开发时, 需要清除IOS本身的各种样式: 1.消除ios按钮原生样式, 给按钮加自定义样式: input[type="button"], input[type=&q ...
- struts2 和 js 标签取值
struts标签是在服务器上替换成html代码的,js是在用户浏览器执行的,这个顺序如果没搞清楚你是搞不好web开发的
- OpenCV 4 Android
OpenCV4Android Want a Quick Start link? Use this tutorial: “OpenCV for Android SDK”. 想要快速开始吗?使用这个教程: ...
- gkd
## Part 0. 开篇 组长博客:[戳我进入]() 作业博客:[班级博客本次作业的链接](https://edu.cnblogs.com/campus/fzu/Grade2016SE/homewo ...