考虑转化题意,我们发现其实就是找一个长度为\(n\)的全排列,使得这个排列有\(A\)个前缀最大值,\(B\)个后缀最大值,求方案数

我们考虑把最大值拎出来单独考虑,同时定义一些数的顺序排列为单调块(随便取的名字)

考虑在这个最大值左边有\(A-1\)个单调块,右边有\(B-1\)个单调块,如果这些块在左右两边按序排好的话就是一种合法方案

那我们只需要找出\(A+B-2\)个单调块,并且将其中拿出\(A-1\)个放在左边,因此答案有一项就是\(C_{A+B-2}^{A-1}\)

考虑怎么从除了最大值外的\(n-1\)个数里挑出单调块,我们仔细分析一下,发现单调块的个数其实就是圆排列的个数

因为对于任意一个圆排列\(a_1,a_2,\cdots,a_k\),我们必然可以拿出其中的最大值\(a_{mx}\),然后\(a_{mx},a_{mx+1},a_k,\cdots,a_1,a_{mx-1}\)就是一个单调块

不难发现这种关系唯一对应,所以答案已经出来了,就是\([_{A+B-2}^{n-1}]\cdot C_{A+B-2}^{A-1}\)

那么对于Luogu P4609 [FJOI2016]建筑师 我们\(O(n^2)\)预处理出第一类斯特林数和组合数的值,然后\(O(1)\)计算就好了

CODE

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=50000,M=200,mod=1e9+7;
int t,n,a,b,C[M+5][M+5],s[N+5][M+5];
inline int sum(CI a,CI b)
{
int t=a+b; return t>=mod?t-mod:t;
}
inline int min(CI a,CI b)
{
return a<b?a:b;
}
inline void init(void)
{
RI i,j; for (s[0][0]=i=1;i<=N;++i) for (j=min(i,M);j;--j)
s[i][j]=sum(s[i-1][j-1],1LL*(i-1)*s[i-1][j]%mod);
for (i=0;i<=M;++i) for (C[i][0]=C[i][i]=j=1;j<i;++j)
C[i][j]=sum(C[i-1][j],C[i-1][j-1]);
}
int main()
{
for (init(),scanf("%d",&t);t;--t)
{
scanf("%d%d%d",&n,&a,&b); if (!a||!b||n-1<a+b-2) { puts("0"); continue; }
printf("%d\n",1LL*s[n-1][a+b-2]*C[a+b-2][a-1]%mod);
}
return 0;
}

考虑对于CF960G Bandit Blues 显然我们不能暴力求出所有的斯特林数,而是只求出一个即可

但是对于一个我们也没有太好的公式去算,因此我们类似于第二类斯特林数,可以用生成函数来算出一行的值

通过数学推导或者OIES,我们得到了第一类斯特林数的生成函数:

\[S_n=\prod_{i=0}^{n-1}(x+i)
\]

最后\(x^k\)的系数就是\([^n_k]\),用分治NTT做到\(O(n\log^2 n)\)。有\(n\log n\)的做法但懒得写了(分治NTT实在好写)

CODE

#include<cstdio>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
#define pb push_back
using namespace std;
typedef vector <int> VT;
const int N=100005,mod=998244353;
int n,a,b;
inline int fact(CI n,int ret=1)
{
for (RI i=1;i<=n;++i) ret=1LL*ret*i%mod; return ret;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline int C(CI n,CI m)
{
return 1LL*fact(n)*quick_pow(1LL*fact(m)*fact(n-m)%mod)%mod;
}
class Poly_Solver
{
private:
int rev[N<<2],lim,p;
inline int sum(CI a,CI b)
{
int t=a+b; return t>=mod?t-mod:t;
}
inline int sub(CI a,CI b)
{
int t=a-b; return t<0?t+mod:t;
}
inline void swap(int& x,int& y)
{
int t=x; x=y; y=t;
}
inline void init(CI n)
{
for (lim=1,p=0;lim<=n;lim<<=1,++p);
for (RI i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<p-1);
}
inline void NTT(int *f,CI opt)
{
RI i; for (i=0;i<lim;++i) if (i<rev[i]) swap(f[i],f[rev[i]]);
for (i=1;i<lim;i<<=1)
{
int m=i<<1,D=quick_pow(3,~opt?(mod-1)/m:mod-1-(mod-1)/m);
for (RI j=0;j<lim;j+=m)
{
int W=1; for (RI k=0;k<i;++k,W=1LL*W*D%mod)
{
int x=f[j+k],y=1LL*f[i+j+k]*W%mod;
f[j+k]=sum(x,y); f[i+j+k]=sub(x,y);
}
}
}
if (!~opt)
{
int Inv=quick_pow(lim); for (RI i=0;i<lim;++i) f[i]=1LL*f[i]*Inv%mod;
}
}
inline VT merge(const VT& VA,const VT& VB)
{
static int A[N<<2],B[N<<2],n,m; VT VC; RI i;
for (n=VA.size(),i=0;i<n;++i) A[i]=VA[i];
for (m=VB.size(),i=0;i<m;++i) B[i]=VB[i];
init(n+m); fill(A+n,A+lim,0); fill(B+m,B+lim,0);
for (NTT(A,1),NTT(B,1),i=0;i<lim;++i) A[i]=1LL*A[i]*B[i]%mod;
for (NTT(A,-1),i=0;i<n+m-1;++i) VC.pb(A[i]); return VC;
}
public:
inline VT solve(CI l,CI r)
{
VT p; if (l==r) return p.pb(l),p.pb(1),p;
int mid=l+r>>1; return merge(solve(l,mid),solve(mid+1,r));
}
}P;
int main()
{
scanf("%d%d%d",&n,&a,&b); if (!a||!b||n-1<a+b-2) return puts("0"),0;
if (n==1) return puts("1"),0; VT ans=P.solve(0,n-2);
return printf("%d",1LL*ans[a+b-2]*C(a+b-2,a-1)%mod),0;
}

Luogu P4609 [FJOI2016]建筑师&&CF 960G Bandit Blues的更多相关文章

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

    传送门 解题思路 好神仙的思路,首先一种排列中按照最高点将左右分开,那么就是要在左边选出\(a-1\)个,右边选出\(b-1\)一个,这个如何计算呢?考虑第一类斯特林数,第一类斯特林数是将\(n\)个 ...

  2. [洛谷P4609] [FJOI2016]建筑师

    洛谷题目链接:[FJOI2016]建筑师 题目描述 小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 \(n\) 个建筑,每个建筑的高度是 \(1\) 到 \(n\) 之间的一 ...

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

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

  4. 洛谷 P4609: [FJOI2016] 建筑师

    本省省选题是需要做的. 题目传送门:洛谷P4609. 题意简述: 求有多少个 \(1\) 到 \(N\) 的排列,满足比之前的所有数都大的数正好有 \(A\) 个,比之后的所有数都大的数正好有 \(B ...

  5. Codeforces 960G. Bandit Blues

    Description 你需要构造一个长度为 \(n\) 的排列 , 使得一个数作为前缀最大值的次数为 \(A\) , 作为后缀最大值的次数为 \(B\) , 求满足要求的排列个数 . 题面 Solu ...

  6. 洛谷P4609 [FJOI2016]建筑师 【第一类斯特林数】

    题目链接 洛谷P4609 题解 感性理解一下: 一神带\(n\)坑 所以我们只需将除了\(n\)外的\(n - 1\)个元素分成\(A + B - 2\)个集合,每个集合选出最大的在一端,剩余进行排列 ...

  7. P4609 [FJOI2016]建筑师

    思路 裸的第一类斯特林数,思路和CF960G相同 预处理组合数和第一类斯特林数回答即可 代码 #include <cstdio> #include <cstring> #inc ...

  8. 洛谷P4609 [FJOI2016]建筑师(第一类斯特林数+组合数)

    题面 洛谷 题解 (图片来源于网络,侵删) 以最高的柱子\(n\)为分界线,我们将左边的一个柱子和它右边的省略号看作一个圆排列,右边的一个柱子和它左边的省略号看作一个圆排列,于是,除了中间的最高的柱子 ...

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

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

随机推荐

  1. 解决 'chromedriver' executable needs to be in PATH.'报错

    试了把chromedriver.exe放到chrome安装文件下,python安装文件下,然后把路径配到path里,均无用. 最后是修改函数调用得以解决: from selenium import w ...

  2. Flutter实战视频-移动电商-10.首页_FlutterSwiper轮播效果制作

    10.首页_FlutterSwiper轮播效果制作 博客地址: https://jspang.com/post/FlutterShop.html#toc-5c2 flutter_swiper http ...

  3. 洛谷 - P2283 - 多边形 - 半平面交

    https://www.luogu.org/problemnew/show/P2283 需要注意max是求解顺序是从右到左,最好保证安全每次都清空就没问题了. #include<bits/std ...

  4. Unresolved function or method require()

    1. 这是在JavaScript配置中没有node.js,去设置中配置就行了,方法如下: setting -> Languages&Frameworks -> Javascript ...

  5. ajaxForm上传文件到本地服务器(封装)

    不啰嗦,直接看代码 1.html: <div class="con-item fix"> <span class="f">文章封面< ...

  6. 黑马MySQL数据库学习day02 表数据CRUD 约束CRUD

    /* 基础查询练习: 1.字段列表查询 当查询全部字段时,一种简便方式,使用*代替全部字段(企业中不推荐使用) 2.去除重复行 DISTINCT,注意修饰的是行,也就是整个字段列表,而不是单个字段. ...

  7. window git bash客户端vimrc设置tab缩进

    从开发机写的代码,弄到windows上的客户端git提交,总是显示格式对不起的问题,问题是再vimrc上tab键的缩进不等于4个空格,然后就需要设置成和linux一样的四格缩进. 安装上git bas ...

  8. [題解](最小生成樹/LCA)luogu_P1967貨車運輸

    一道好題不出所料又抄的題解 1.首先對於這張圖肯定要考慮走哪些邊不走哪些邊,發現我們想要的肯定那些邊權最大的邊,所以想到最大生成樹 這樣能保證選到盡量大的邊 2.跑完最大生成樹后每兩點之間就有唯一路徑 ...

  9. log4j打印错误日志输出 利用sql取出的值放在list集合中,集合中的字段类型为映射类类型

    public ServiceResult<List<KefuDetail>> MaxRespondtime(Date startDate,Date endDate, Strin ...

  10. 120 Triangle 三角形最小路径和

    给出一个三角形(数据数组),找出从上往下的最小路径和.每一步只能移动到下一行中的相邻结点上.比如,给你如下三角形:[     [2],    [3,4],   [6,5,7],  [4,1,8,3]] ...