题目

CF960G

做法

设\(f(i,j)\)为\(i\)个数的序列,有\(j\)个前缀最大值的方案数

我们考虑每次添一个最小数,则有:\(f(i,j)=f(i-1,j)+(i-1)*f(i-1,j-1)\),显然这是第一类斯特林数

从而我们得到一个朴素的答案:$$Ans=\sum\limits_{i=1}{n}f_{i,a-1}×f_{n-1-i,b-1}×C_{n-1}i$$

理解:枚举\(i+1\)为最大值添的位置,则已限制了前缀最值个数及后缀最值个数,然后再乘上前半部分所填的数

观察\(f_{i,a-1}×f_{n-1-i,b-1}\),发现第一维和唯一:$$Ans=\begin{bmatrix}n-1\a+b-2\end{bmatrix}C_{a+b-2}^{a-1}$$

可能会有点难理解:等同于分类成\(a+b-2\)个环,而环是不考虑顺序的,所以我们选择不考虑打乱顺序地选择环

至此,我们唯一需要的就是快速求出第一类斯特林数\(\begin{bmatrix}n-1\\a+b-2\end{bmatrix}\)

即使是单个数也无法有特殊的公式快速得出,所以我们用与求整行第一类斯特林数的方法求出

\(O(nlog^2n)\)的方法已经烂大街了,分治一下就行

\(O(nlogn)\):由于涉及到公式推倒,不是本题解的重点

移步浅谈斯特林数与斯特林反演,内有详细证明推倒及代码

Code

  1. #include<bits/stdc++.h>
  2. typedef int LL;
  3. const LL mod=998244353,g=3,_g=332748118,maxn=2e5+9;
  4. inline LL Pow(LL base,LL b){
  5. LL ret(1);
  6. while(b){
  7. if(b&1) ret=1ll*ret*base%mod; base=1ll*base*base%mod; b>>=1;
  8. }return ret;
  9. }
  10. LL r[maxn],W[maxn];
  11. inline LL Fir(LL n){
  12. LL limit(1),len(0),up(n<<1);
  13. while(limit<up){
  14. limit<<=1; ++len;
  15. }
  16. for(LL i=0;i<limit;++i) r[i]=(r[i>>1]>>1)|((i&1)<<len-1);
  17. return limit;
  18. }
  19. inline void NTT(LL *a,LL n,LL type){
  20. for(LL i=0;i<n;++i) if(i<r[i]) std::swap(a[i],a[r[i]]);
  21. for(LL mid=1;mid<n;mid<<=1){
  22. LL wn(Pow(type?g:_g,(mod-1)/(mid<<1)));
  23. W[0]=1; for(LL i=1;i<mid;++i) W[i]=1ll*W[i-1]*wn%mod;
  24. for(LL R=mid<<1,j=0;j<n;j+=R)
  25. for(LL k=0;k<mid;++k){
  26. LL x(a[j+k]),y(1ll*W[k]*a[j+mid+k]%mod);
  27. a[j+k]=1ll*(x+y)%mod; a[j+mid+k]=1ll*(x-y+mod)%mod;
  28. }
  29. }
  30. }
  31. LL T[maxn],F[maxn],H[maxn],fac[maxn],fav[maxn],tmp[maxn],sum[maxn],B[maxn];
  32. inline LL Mul(LL n,LL *a,LL *b,LL *ans){
  33. LL limit(Fir(n));
  34. NTT(a,limit,1); NTT(b,limit,1);
  35. for(LL i=0;i<limit;++i) ans[i]=1ll*a[i]*b[i]%mod;
  36. NTT(ans,limit,0);
  37. for(LL i=((n-1)<<1)+1;i<limit;++i) a[i]=b[i]=0;
  38. return Pow(limit,mod-2);
  39. }
  40. inline void Solve(LL n,LL *a){
  41. if(!n){ a[0]=1; return; }
  42. if(n==1){ a[1]=1; return; }
  43. LL len(n/2);
  44. Solve(len,a);
  45. for(LL i=0;i<=len;++i){
  46. F[i]=1ll*Pow(len,i)*fav[i]%mod;
  47. H[i]=1ll*fac[i]*a[i]%mod;
  48. }
  49. std::reverse(H,H+len+1);
  50. LL limit(Fir(len+1));
  51. NTT(F,limit,1); NTT(H,limit,1);
  52. for(LL i=0;i<limit;++i) F[i]=1ll*F[i]*H[i]%mod;
  53. NTT(F,limit,0);
  54. LL ty(Pow(limit,mod-2));
  55. for(LL i=0;i<=len;++i) tmp[i]=1ll*F[len-i]*ty%mod*Pow(fac[i],mod-2)%mod;
  56. for(LL i=(len<<1);i<=limit;++i) F[i]=H[i]=0;
  57. LL val(Mul(len+1,a,tmp,B));
  58. for(LL i=0;i<=(len<<1);++i) a[i]=1ll*B[i]*val%mod;
  59. if(n&1)
  60. for(LL i=n;i>=1;--i) a[i]=1ll*(a[i-1]+1ll*(n-1)*a[i]%mod)%mod;
  61. }
  62. LL n,a,b,m;
  63. LL ans[maxn];
  64. int main(){
  65. scanf("%d%d%d",&n,&a,&b);
  66. LL val;
  67. val=fac[0]=fac[1]=1;
  68. for(LL i=2;i<=n;++i) val=fac[i]=1ll*val*i%mod;
  69. val=fav[n]=Pow(fac[n],mod-2);
  70. for(LL i=n;i>=1;--i) val=fav[i-1]=1ll*val*i%mod;
  71. Solve(n-1,ans);
  72. n=a+b-2; m=a-1;
  73. printf("%d\n",1ll*ans[n]*fac[n]%mod*fav[m]%mod*fav[n-m]%mod%mod);
  74. }

CF960G(第一类斯特林数)的更多相关文章

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

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

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

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

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

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

  4. CF960G Bandit Blues 分治+NTT(第一类斯特林数)

    $ \color{#0066ff}{ 题目描述 }$ 给你三个正整数 \(n\),\(a\),\(b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大 ...

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

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

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

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

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

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

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

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

  9. P4609 [FJOI2016]建筑师(第一类斯特林数)

    传送门 没想到连黑题都会有双倍经验的 其实这题本质上是和CF960G Bandit Blues一样的,不过那里是要用分治FFT预处理第一类斯特林数,这里直接打表预处理第一类斯特林数就可以了 //min ...

随机推荐

  1. (转)SQL执行顺序

    SQL语句理解:http://blog.jobbole.com/55086/ 窗口函数/分析函数:http://blog.csdn.net/mfkpie/article/details/1636451 ...

  2. 视觉slam闭环检测之-DBoW2 -视觉词袋构建

    需要准备的知识点:http://www.cnblogs.com/zjiaxing/p/5616653.html      http://www.cnblogs.com/zjiaxing/p/56166 ...

  3. js数组的方法

    arrayObject.join(separator) 将数组以separator字符为间隔转化为字符串并返回,如果不传,默认为逗号.此方法不会改变原数组 let arr = [1,2,3]; arr ...

  4. HTML-HTML链接JavaScript的几种方法

    把JavaScript文件放在head中 标准方法是把JavaScript文件放到head标签内. <head> <script type="text/javascript ...

  5. android应用安全——代码安全(android代码混淆)

    android2.3的SDK开始在eclipse中支持代码混淆功能(理论上java都支持混淆,但关键在于如何编写proguard的混淆脚本,2.3的SDK使用简单的配置就可以实现混淆).使用SDK2. ...

  6. ES6学习之Babel的正确安装姿势

    开始学习ES6,写点东西放上博客^_^ 本文介绍Babel6.x的安装过程~ 首先呢,可以使用Babel在线转换 https://babeljs.io/repl/ 然后进入主题:安装Babel(命令行 ...

  7. 【Lombok】了解

    项目中使用了 Lombok ,对象无需写get set 等方法,一个注释便可以搞定.IDEA中项目报错,下载对应插件(Lombok Plugin)就好了.很神奇,就了解一下: 官网: Project ...

  8. 巨蟒python全栈开发数据库前端6:事件onclick的两种绑定方式&&onblur和onfocus事件&&window.onload解释&&小米商城讲解

    1.回顾上节内容(JavaScript) 一.JavaScript概述 1.ECMAScript和JavaScript的关系 2.ECMAScript的历史 3.JavaScript是一门前后端都可以 ...

  9. 常见面试题整理--Python概念篇

    希望此文可以长期更新并作为一篇Python的面试宝典.每一道题目都附有详细解答,以及更加详细的回答链接.此篇是概念篇,下一篇会更新面试题代码篇. (一).这两个参数是什么意思:*args,**kwar ...

  10. 手势识别:GestureDetector

    当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等等,我们知道View类有个View.OnTouchListener接口,通过重写他的onTouch(View v, ...