题面1 题面2

两个题推导是一样的,具体实现不一样,所以写一起了,以FJOI 2016 建筑师 的题面为标准

前后在组合意义下一样,现在只考虑前面,可以发现看到的这a个建筑将这一段划分成了a-1个区间,区间里的数随意填。

看起来可以用组合数算,但是还要考虑看到的建筑,所以我们把每个建筑和它后面这段区间合起来看。设区间的长度是len,这就是一个len+1个数的圆排列(等于len!,相当于固定一个开头后面随便排)

这样考虑前后就是将n-1个数划分为a+b-2个全排列,n-1是因为最高的那个在两边都没被算进去,是第一类斯特林数。同时a+b-2个圆排列之间还要选出来a-1个放前面,乘上组合数即可。现在关键是如何求第一类斯特林数。

FJOI那道题n*k不是很大,可以直接$O(nk)$递推,边界条件是$S[0][0]=1$,转移是$S[i][j]=S[i-1][j-1]+S[i-1][j]*(i-1)$

CF那道题只有一次询问,但是n,k都很大,我们考虑第一类斯特林数在第一维固定下的生成函数:$G(x)=\prod\limits_{i=0}^{n-1}(x+i)$,x是从$s[i-1][j-1]$来的,i-1是从$s[i-1][j]*(i-1)$来的。用分治NTT优化

Code1

 //Simple NTT
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define vint vector<int>
using namespace std;
const int N=,mod=;
int fac[N],inv[N],rev[N];
int aa[N],bb[N],pw[][];
int a,b,n,l1,l2,G,Gi,Ni; vint stl;
int Qpow(int x,int k)
{
if(k==) return x;
int tmp=Qpow(x,k/);
return k%?1ll*tmp*tmp%mod*x%mod:1ll*tmp*tmp%mod;
}
int C(int a,int b)
{
return 1ll*fac[a]*inv[b]%mod*inv[a-b]%mod;
}
void SPJ()
{
if(!a||!b||n-<a+b-) printf(""),exit();
if(n==) printf(""),exit();
}
void Pre()
{
fac[]=inv[]=,G=,Gi=Qpow(G,mod-);
for(int i=;i<=;i++)
{
pw[i][]=Qpow(G,(mod-)/(<<i));
pw[i][]=Qpow(Gi,(mod-)/(<<i));
}
for(int i=;i<=a+b;i++) fac[i]=1ll*fac[i-]*i%mod;
inv[a+b]=Qpow(fac[a+b],mod-);
for(int i=a+b-;i;i--) inv[i]=1ll*inv[i+]*(i+)%mod;
}
void Prework(vint &a,vint &b)
{
register int i;
l1=a.size()-,l2=b.size()-;
l1+=l2,l2=; while(l2<=l1) l2<<=;
a.resize(l2),b.resize(l2);
for(i=;i<=l2;i++) aa[i]=a[i];
for(i=;i<=l2;i++) bb[i]=b[i];
for(i=;i<l2;i++)
rev[i]=(rev[i>>]>>)+(i&)*(l2>>);
}
void Trans(int *arr,int len,int typ)
{
register int i,j,k;
for(i=;i<len;i++)
if(rev[i]>i) swap(arr[rev[i]],arr[i]);
for(i=;i<=len;i<<=)
{
int lth=i>>,ort=pw[(int)log2(i)][typ==-];
for(j=;j<len;j+=i)
{
int ori=,tmp;
for(k=j;k<j+lth;k++,ori=1ll*ori*ort%mod)
{
tmp=1ll*ori*arr[k+lth]%mod;
arr[k+lth]=(arr[k]-tmp+mod)%mod;
arr[k]=(arr[k]+tmp)%mod;
}
}
}
if(typ==-)
{
int Ni=Qpow(len,mod-);
for(i=;i<=len;i++)
arr[i]=1ll*arr[i]*Ni%mod;
}
}
vint NTT(vint a,vint b)
{
Prework(a,b);
Trans(aa,l2,),Trans(bb,l2,);
for(int i=;i<l2;i++) aa[i]=1ll*aa[i]*bb[i]%mod;
Trans(aa,l2,-);
vint ret; ret.clear();
for(int i=;i<l2;i++) ret.push_back(aa[i]);
return ret;
}
vint CDQ(int l,int r)
{
if(l==r) return {l,};
else
{
int mid=(l+r)/;
vint a1=CDQ(l,mid),a2=CDQ(mid+,r);
return NTT(a1,a2);
}
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
SPJ(),Pre(),stl=CDQ(,n-);
printf("%d",1ll*C(a+b-,a-)*stl[a+b-]%mod);
return ;
}

Code2

 #include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,M=,mod=1e9+;
int fac[N],inv[N],S[N][M];
int a,b,n,T,l1,l2,G,Gi,Ni;
int Qpow(int x,int k)
{
if(k==) return x;
int tmp=Qpow(x,k/);
return k%?1ll*tmp*tmp%mod*x%mod:1ll*tmp*tmp%mod;
}
int C(int a,int b)
{
return 1ll*fac[a]*inv[b]%mod*inv[a-b]%mod;
}
bool SPJ()
{
if(!a||!b||n-<a+b-)
{puts(""); return false;}
if(n==)
{puts(""); return false;}
return true;
}
void Pre()
{
register int i,j;
fac[]=inv[]=,S[][]=;
for(i=;i<=;i++) fac[i]=1ll*fac[i-]*i%mod;
inv[]=Qpow(fac[],mod-);
for(i=;i;i--) inv[i]=1ll*inv[i+]*(i+)%mod;
for(i=;i<=;i++)
for(j=;j<=;j++)
S[i][j]=(S[i-][j-]+1ll*(i-)*S[i-][j]%mod)%mod;
}
int main()
{
Pre();
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&a,&b);
if(SPJ()) printf("%lld\n",1ll*C(a+b-,a-)*S[n-][a+b-]%mod);
}
return ;
}

解题:CF960G Bandit Blues & FJOI 2016 建筑师的更多相关文章

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

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

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

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

  3. [CF960G] Bandit Blues

    题意 给你三个正整数 \(n,a,b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大值的数的个数,求长度为 \(n\) 的排列中满足 \(A = a ...

  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]Bandit Blues(第一类斯特林数+分治卷积)

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

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

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

  8. Luogu P4609 [FJOI2016]建筑师&&CF 960G Bandit Blues

    考虑转化题意,我们发现其实就是找一个长度为\(n\)的全排列,使得这个排列有\(A\)个前缀最大值,\(B\)个后缀最大值,求方案数 我们考虑把最大值拎出来单独考虑,同时定义一些数的顺序排列为单调块( ...

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

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

随机推荐

  1. kettle学习笔记(八)——kettle查询步骤与连接步骤

    一.概述 查询步骤: 用来查询数据源里的数据并合并到主数据流中 . 连接步骤: 结果集通过关键字进行连接 .(与前面的UNION不同) 二.查询步骤 1.流查询 流查询示例:(注意上文中的流查询的限制 ...

  2. 20155206《网络对抗》Web安全基础实践

    20155206<网络对抗>Web安全基础实践 实验后问题回答 (1)SQL注入攻击原理,如何防御 攻击原理:SQL注入攻击就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查 ...

  3. 20155308《网络攻防》 Exp1 PC平台逆向破解(5)M

    20155308<网络攻防> Exp1 PC平台逆向破解(5)M 逆向及Bof基础实践说明 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是 ...

  4. 20155323刘威良 网络对抗《网络攻防》 Exp1 PC平台逆向破解(5)M

    实践目标 本次实践的对象是linux的可执行文件 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另一个代码片段,getShell,会返回一个可 ...

  5. Oracle出现与并行相关的ORA-00600时的调查方法

    出现了 ORA-00600[kxfpqsod_qc_sod], 如何调查呢? 例如:从trace 文件的 Call Stack,可以看到 Error: ORA-600 [kxfpqsod_qc_sod ...

  6. Redis基本数据类型介绍笔记

    Redis 数据类型 Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合). String(字符串) st ...

  7. Meteor入门介绍

    Meteor是什么 基于nodejs的实时web APP开发框架. Meteor能带来什么 简单的说,你可以用js搞定客户端.服务端的开发.另外,客户端.服务端的界限被极大的模糊.客户端的界面跟服务端 ...

  8. Salesforce随笔: 将Visualforce Page渲染为PDF文件(Render a Visualforce Page as a PDF File)

    参照 : Visualforce Developer Guide 第60页 <Render a Visualforce Page as a PDF File> 你可以用PDF渲染服务生成一 ...

  9. Jmeter(十九)_ForEach控制器实现网页爬虫

    一直以来,爬虫似乎都是写代码去实现的,今天像大家介绍一下Jmeter如何实现一个网页爬虫! 龙渊阁测试开发家园 317765580 Jmeter的爬虫原理其实很简单,就是对网页提交一个请求,然后把返回 ...

  10. GitHub 新手教程 六,Git GUI 新手教程(3),从GitHub远端同步代码库

    从GitHub把代码库下载到本地: 1,打开 GitGUI,单击我们之前克隆好的本地库: 2,按图片所示点击,同步远端代码: 3,出现如下提示后,点击“Close”: 4,上面只是把代码下载下来,还没 ...