【CF960G】Bandit Blues(第一类斯特林数,FFT)
【CF960G】Bandit Blues(第一类斯特林数,FFT)
题面
洛谷
CF
求前缀最大值有\(a\)个,后缀最大值有\(b\)个的长度为\(n\)的排列个数。
题解
完完全全就是【FJOI】建筑师的加强版本。
显然每一个前缀最大值和一段连续的区间构成了一个环排列,显然每个前缀最大值就是这个环中的最大值。而全局最大值一定把前后缀最大值分开。
所以答案考虑除最大值外,左侧需要\(a-1\)个前缀最大值,右侧需要\(b-1\)个前缀最大值。也就是一共要\(a+b-2\)个环,那么这一部分的贡献是\(\begin{bmatrix}n-1\\a+b-2\end{bmatrix}\)。而环在左右随意分配,所以再乘上一个\(a+b-2\choose a-1\)。
解释一个小问题,为什么不需要考虑环的最大值的大小关系,因为我们强制在排列过程中按照最大值从小往大放,而每个环因为放置的时候是一个线段,那么我们保证最大值一定在靠外侧,这样子后面的比它小的值必定不是前缀或者后缀最大值。
那么问题转化成了怎么预处理第一类斯特林数。戳这里
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAX 300000
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
return s;
}
int r[MAX],W[MAX];
void NTT(int *P,int opt,int N)
{
int l=0;for(int i=1;i<N;i<<=1)++l;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
{
int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
}
}
if(opt==-1)
{
reverse(&P[1],&P[N]);
for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
}
}
int S[MAX],jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){if(n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int A[MAX],B[MAX],pw[MAX];
void Solve(int len)
{
if(len==0){S[0]=1;return;}
if(len==1){S[1]=1;return;}
if(len&1)
{
Solve(len-1);
for(int i=len;i;--i)S[i]=(S[i-1]+1ll*S[i]*(len-1))%MOD;
}
else
{
Solve(len>>1);int l=len>>1,N;
for(N=1;N<=len;N<<=1);
pw[0]=1;for(int i=1;i<=l;++i)pw[i]=1ll*pw[i-1]*l%MOD;
for(int i=0;i<=l;++i)A[i]=1ll*S[i]*jc[i]%MOD;
for(int i=0;i<=l;++i)B[i]=1ll*pw[i]*jv[i]%MOD;
reverse(&B[0],&B[l+1]);
NTT(A,1,N);NTT(B,1,N);
for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;
NTT(A,-1,N);
for(int i=0;i<=l;++i)A[i]=1ll*A[i+l]*jv[i]%MOD;
for(int i=l+1;i<N;++i)A[i]=B[i]=0;
for(int i=0;i<=l;++i)B[i]=S[i];
NTT(A,1,N);NTT(B,1,N);
for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;
NTT(A,-1,N);
for(int i=0;i<=len;++i)S[i]=A[i];
for(int i=0;i<N;++i)A[i]=B[i]=0;
}
}
int n,a,b;
int main()
{
n=read();a=read();b=read();
jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=1;i<=max(a+b,n);++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=2;i<=max(a+b,n);++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=max(a+b,n);++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
Solve(n-1);int ans=1ll*C(a+b-2,a-1)*S[a+b-2]%MOD;
printf("%d\n",ans);
return 0;
}
【CF960G】Bandit Blues(第一类斯特林数,FFT)的更多相关文章
- CF960G Bandit Blues 第一类斯特林数+分治+FFT
题目传送门 https://codeforces.com/contest/960/problem/G 题解 首先整个排列的最大值一定是 \(A\) 个前缀最大值的最后一个,也是 \(B\) 个后缀最大 ...
- 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 ...
- Codeforces960G Bandit Blues 【斯特林数】【FFT】
题目大意: 求满足比之前的任何数小的有A个,比之后的任何数小的有B个的长度为n的排列个数. 题目分析: 首先写出递推式,设s(n,k)表示长度为n的排列,比之前的数小的数有k个. 我们假设新加入的数为 ...
- CF960G Bandit Blues 【第一类斯特林数 + 分治NTT】
题目链接 CF960G 题解 同FJOI2016只不过数据范围变大了 考虑如何预处理第一类斯特林数 性质 \[x^{\overline{n}} = \sum\limits_{i = 0}^{n}\be ...
- CF960G Bandit Blues(第一类斯特林数)
传送门 可以去看看litble巨巨关于第一类斯特林数的总结 设\(f(i,j)\)为\(i\)个数的排列中有\(j\)个数是前缀最大数的方案数,枚举最小的数的位置,则有递推式\(f(i,j)=f(i- ...
- CF960G Bandit Blues 分治+NTT(第一类斯特林数)
$ \color{#0066ff}{ 题目描述 }$ 给你三个正整数 \(n\),\(a\),\(b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大 ...
- 【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 ...
随机推荐
- Window环境下配置MySQL 5.6的主从复制
原文:Window环境下配置MySQL 5.6的主从复制 1.环境准备 Windows 7 64位 MySQL 5.6 主库:192.168.103.207 从库:192.168.103.208 2. ...
- [Spark][Hive]外部文件导入到Hive的例子
外部文件导入到Hive的例子: [training@localhost ~]$ cd ~[training@localhost ~]$ pwd/home/training[training@local ...
- 【php增删改查实例】第十七节 - 用户登录(1)
新建一个login文件,里面存放的就是用户登录的模块. <html> <head> <meta charset="utf-8"> <sty ...
- VS2010、VS2012、VS2013、VS2015、VS2017各版本产品激活秘钥
Visual Studio 2017(VS2017) 企业版 Enterprise 注册码:NJVYC-BMHX2-G77MM-4XJMR-6Q8QF Visual Studio 2017(VS201 ...
- 行业干货-如何逆向解决QT程序汉化中乱码问题
前言 “一款QT开发的国外软件,大概率是没有做中文支持的,所以你汉化中,不论怎么设置编码都一定是乱码.面对这个问题,你去互联网上找答案,答案却大多是复制粘贴的开发中解决乱码的文章,可是我们是要逆向中解 ...
- Python-习题-11
1,内容回顾列表:增 append insert extend 删 remove pop clear del 改 li[索引] = '被修改的内容' li[切片]:'被修改的内容' 查 for循环 r ...
- 2016.3.30 OneZero站立会议
会议时间:2016年3月30日 13:00~13:20 会议成员:冉华,张敏,王巍,夏一鸣. 会议目的:汇报前一天工作,全体成员评论,确定会议内容或分配下一步任务. 会议内容: 1.汇报头一天工作情 ...
- Linux内核分析作业 NO.7
可执行程序的装载 于佳心 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实 ...
- linux内核分析实践二学习笔记
Linux实践二--内核模块的编译 标签(空格分隔): 20135328陈都 理解内核的作用 Linux内核[kernel]是整个操作系统的最底层,它负责整个硬件的驱动,以及提供各种系统所需的核心功能 ...
- C# wkhtmltopdf 将html转pdf(详解)
https://www.cnblogs.com/louby/p/905198.html转自,看文章只放了代码看起来云里雾里的,在此做些解析 使用说明: 1.首先呢,得安装下软件,地址下面有链接,文件里 ...