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 ...
随机推荐
- HQL多表查询
------------------siwuxie095 HQL 多表查询 以客户和联系人为例(一对多) 1.内连接 (1)hql 语句写法 from Customer c inner join c. ...
- SpringBoot Actuator & SpringBoot Admin
SpringBoot Actuator提供了很多监控和管理你的spring boot应用的HTTP或者JMX端点,并且你可以有选择地开启和关闭部分功能. 当你的spring boot应用中引入依赖之后 ...
- php使用curl 实现GET和POST请求(抓取网页,上传文件),支持跨项目和跨服务器
一:curl 函数和参数详解 函数库:1:curl_init 初始化一个curl会话2:curl_close 关闭一个curl会话3:curl_setopt 为一个curl设置会话参数4:curl_e ...
- Window: move\copy\xcopy
Move 移动文件和重命名文件与目录. 要移动一个或多个文件: MOVE [/Y | /-Y] [drive:][path]filename1[,...] destination 要重命名目录: MO ...
- Python爬虫实战五之模拟登录淘宝并获取所有订单
经过多次尝试,模拟登录淘宝终于成功了,实在是不容易,淘宝的登录加密和验证太复杂了,煞费苦心,在此写出来和大家一起分享,希望大家支持. 温馨提示 更新时间,2016-02-01,现在淘宝换成了滑块验证了 ...
- C#变量初始化
在C#中声明变量使用下述语法: datatype identifier;, 例如: int i; 该语句声明int变量i.编译器不允许在表达式中使用这个变量,除非用一个值初始化了改变量.如果你不需要使 ...
- 如何将.crt的ssl证书文件转换成.pem格式
如何将.crt的ssl证书文件转换成.pem格式 摘自:https://www.landui.com/help/show-8127 2018-07-04 14:55:41 2158次 准备:有一台安装 ...
- Arch Linux 使用markdown
Arch Linux 使用markdown pandoc 文档格式转换 pygments 代码高亮 markdown-mode.el 配置emacs pandoc 号称文件格式转换的瑞士军刀,这里主要 ...
- 分布式缓存系统Memcached简介与以及在.net下的实践(转)
缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...
- Shell脚本中$0、$?、$!、$$、$*、$#、$@
1. $$Shell本身的PID(ProcessID) 2. $!Shell最后运行的后台Process的PID 3. $?最后运行的命令的结束代码(返回值) 4. $-使用Set命令设定的Flag一 ...