CF960G Bandit Blues

本文介绍了一种算法,用于计算在1...n的全排列中,不同前缀最大值的个数为a,不同后缀最大值的个数为b的排列数量。通过分析排列特性,利用第一类斯特林数和快速傅里叶变换(NTT),实现了高效求解。

Description

题目链接

计算 \(1...n\) 的全排列中不同的前缀最大值的个数等于 \(a\) 且不同的后缀最大值的个数等于 \(b\) 的排列的数目对 \(998244353\) 取模的结果

\(1\le n\le 10^5,0\le a,b\le n​\)

Solution

画一画合法的排列可以分析得出以下信息

  • 无论是前缀最大值还是后缀最大值,新出现的最大值 \(k​\) 总是会以 \(\{k,i_1,i_2,...,i_c\}​\) 的形式出现,其中 \(i_1,i_2,...,i_c​\) 为任意 \(c​\) 个小于 \(k​\) 的数,\(c​\) 可以为 \(0​\)

  • 整个排列的最大值,即 \(n​\),会将这个序列分为前后两个排列,而这两个排列是独立的

  • 由于我们只关注排列的相对大小关系,所以被处在下标 \(i​\) 位置的 \(n​\) 划分出的两个小排列可以直接看作 \(\{1,2,...,i-1\}​\)\(\{1,2,...,n-i\}​\) 两组元素的排列

所以我们只需要求 \(1...i\) 的全排列中不同的前缀最大值的个数等于 \(s\) 的排列个数即可,\(i\in [0,n],s\in[1,n]\)

从第一条性质我们可以看出,如果我们将 \(n\) 个元素划分为 \(j\) 个集合,并默认其中的最大值为 \(k\) ,并按每个集合的 \(k\) 将这 \(j\) 个集合从小到大排序,那么这一定是一种符合条件的方案,并且这种构造方法可以包括所有的合法方案

但是如果这样用第二类斯特林数做的话,对于每一个大小为 \(s\) 的集合,我们还需乘上 \((s-1)!\),这样就不太好做了

实际上第一类斯特林数可以很好地解决这个问题,因为它本质上是枚举将长度为 \(n\) 的序列分解为 \(i\) 个循环的方案数,符合我们的要求

所以我们最终要求的是 \(\begin{bmatrix}i\\a-1\end{bmatrix}\)\(\begin{bmatrix}i\\b-1\end{bmatrix}\),其中 \(i\in [0,n-1]\)

答案就是 \(\text{ans}=\sum\limits_{i=1}^{n}\begin{bmatrix}i-1\\a-1\end{bmatrix}\begin{bmatrix}n-i\\b-1\end{bmatrix}​\)

那么怎么快速求第一类斯特林数呢?

我们知道 \(\begin{bmatrix}n\\i\end{bmatrix}\) 的生成函数是 \(x^{\overline{n}}\),但这只能快速求一行的第一类斯特林数,对于求一列的话,复杂度就退化成 \(O(n^2\log n)\)

然后发现没有快速求一列第一类斯特林数的方法。。。(其实是我不会)

从另一个角度思考问题,不如忽略掉第三条性质,直接生成 \(a+b-2\) 个循环再分配到两边,这样也是对的

那么答案就是 \(\text{ans}=\begin{bmatrix}n-1\\a+b-2\end{bmatrix}\dbinom{a+b-2}{a-1}\)

组合数很好求,下面具体说说如何求第一类斯特林数

因为 \(\begin{bmatrix}n\\i\end{bmatrix}=[x^i](x(x+1)(x+2)...(x+n-1))\) ,所以关键是如何求这 \(n\) 个多项式的卷积

考虑倍增

假设现在已经求出了 \(x(x+1)(x+2)...(x+k-1)\) ,需要求 \(x(x+1)(x+2)...(x+k-1)(x+k)(x+k+1)...(x+2k-1)\)

显然后一半的式子可以通过将 \(x+k\) 带入前一半的式子得到,然后再把两个式子 \(\text{NTT}\) 一下就可以得到我们想要的式子了

怎么带入呢?考虑二项式定理,那么对于 \(p\) 次项,它的系数为
\[ \sum\limits_{i=p}^{k}a_i\dbinom{i}{p}k^{i-p}\tag{1} \]
其中 \(a_i\) 为带入前第 \(i\) 次项的系数

展开这个式子,得到
\[ \frac{1}{p!}\sum\limits_{i=p}^{k}a_ii!\frac{k^{i-p}}{(i-p)!}\tag{2} \]
定义 \(f_i=a_ii!,g_i=\frac{k^i}{i!}\),那么带入后第 \(p\) 次项系数 \(a'_p\)
\[ a'_p=\frac{1}{p!}\sum\limits_{i=p}^{k}f_ig_{i-p}\tag{3} \]
\(g_i\) 中的值全部翻转,得 \(g'_i\),那么
\[ a'_p=\frac{1}{p!}\sum\limits_{i=0}^{k-p}f_{k-i}g'_{p+i}\tag{4} \]
也可以写成
\[ a'_p=\frac{1}{p!}\sum\limits_{i=0}^{k+p}f_ig'_{k+p-i}\tag{5} \]
然后就能 \(\text{NTT}​\) 求出系数了

复杂度 \(O(n\log n)\)

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int mod=998244353;
const int G=3;
const int invG=332748118;
int n,A,B,fac[N<<1],inv[N<<1],f[N<<2],g[N<<2],a[N<<2],b[N<<2],k,now,INV;
inline void Preprocess(){
    fac[0]=1;for(register int i=1;i<=(n<<1);i++)fac[i]=1ll*fac[i-1]*i%mod;
    inv[0]=inv[1]=1;for(register int i=2;i<=(n<<1);i++)inv[i]=(-1ll*mod/i*inv[mod%i]%mod+mod)%mod;
    for(register int i=2;i<=(n<<1);i++)inv[i]=1ll*inv[i-1]*inv[i]%mod;
}
inline int C(int n,int m){if(n<0||m<0||n<m)return 0;return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline int fas(int x,int p){int res=1;while(p){if(p&1)res=1ll*res*x%mod;p>>=1;x=1ll*x*x%mod;}return res;}
inline int MOD(int x){x-=x>=mod? mod:0;return x;}
inline void NTT(int *a,int f){
    for(register int i=0,j=0;i<k;i++){
        if(i>j)swap(a[i],a[j]);
        for(register int l=k>>1;(j^=l)<l;l>>=1);}
    for(register int i=1;i<k;i<<=1){
        int w=fas(~f? G:invG,(mod-1)/(i<<1));
        for(register int j=0;j<k;j+=(i<<1)){
            int e=1;
            for(register int p=0;p<i;p++,e=1ll*e*w%mod){
                int x=a[j+p],y=1ll*a[j+p+i]*e%mod;
                a[j+p]=MOD(x+y);a[j+p+i]=MOD(x-y+mod);
            }
        }
    }
}
inline void Solve(int m){
    if(m==1){f[1]=1;return;}
    int M=m>>1;Solve(M);
    for(register int i=0;i<=M;i++)a[i]=1ll*f[i]*fac[i]%mod;
    now=1;
    for(register int i=0;i<=M;i++)
        b[i]=1ll*now*inv[i]%mod,now=1ll*now*M%mod;
    reverse(b,b+M+1);
    k=1;while(k<=M+M)k<<=1;INV=fas(k,mod-2);
    for(register int i=M+1;i<k;i++)a[i]=b[i]=0;
    NTT(a,1);NTT(b,1);
    for(register int i=0;i<k;i++)a[i]=1ll*a[i]*b[i]%mod;
    NTT(a,-1);
    for(register int i=0;i<k;i++)a[i]=1ll*a[i]*INV%mod;
    for(register int i=0;i<=M;i++)g[i]=1ll*inv[i]*a[M+i]%mod;
    for(register int i=M+1;i<k;i++)g[i]=0;
    NTT(f,1);NTT(g,1);
    for(register int i=0;i<k;i++)f[i]=1ll*f[i]*g[i]%mod;
    NTT(f,-1);
    for(register int i=0;i<=(M<<1);i++)f[i]=1ll*f[i]*INV%mod;
    if((M<<1)!=m){
        for(register int i=m;i;i--)
            f[i]=MOD(f[i-1]+1ll*(m-1)*f[i]%mod);
        f[0]=1ll*f[0]*(m-1)%mod;
    }
}
int main(){
    scanf("%d%d%d",&n,&A,&B);
    if(A+B-2<0||A-1<0||B-1<0||A+B-2>n-1){puts("0");return 0;}
    Preprocess();
    if(n==1){if(A+B-2==0)printf("%d\n",C(A+B-2,A-1));else puts("0");return 0;}
    Solve(n-1);
    printf("%lld\n",1ll*f[A+B-2]*C(A+B-2,A-1)%mod);
    return 0;
}

转载于:https://www.cnblogs.com/ForwardFuture/p/11522786.html

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样与统计,通过模拟系统元件的故障与修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构与设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码与案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行与可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理与实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估与优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
03-29
<think>好的,我需要回答用户关于“Bandit”在IT安全或编程上下文中的问题。首先,用户可能指的是两种不同的“Bandit”:一种是安全领域的工具,另一种是编程学习平台。我需要确认这两方面的信息,并分别解释清楚。 首先,回忆一下Bandit在安全领域的应用。Bandit通常是一个静态代码分析工具,用于查找Python代码中的安全漏洞。它属于SAST(静态应用程序安全测试)工具,帮助开发者在开发阶段发现潜在的安全问题,如SQL注入、硬编码密码等。这个工具对于提高代码安全性很有帮助,特别是在持续集成流程中使用。 然后,编程学习平台方面,可能是指OverTheWire的Bandit游戏。这是一个面向初学者的网络安全挑战,通过SSH连接到不同关卡,学习基本的Linux命令和安全概念。用户通过完成任务提升到下一级别,适合学习实际操作和渗透测试的基础知识。 接下来,需要确保回答的结构清晰,分点说明两种不同的Bandit。同时,根据用户之前的示例,可能需要生成相关问题。此外,注意使用正确的中文表达,避免技术术语使用不当,同时保持口语化。 需要验证Bandit工具的具体功能和特点,比如它支持哪些漏洞检测,如何集成到开发流程中。对于OverTheWire的Bandit,确认关卡设计和学习目标,确保信息准确。同时,检查是否有其他可能的Bandit相关工具或框架,但根据已有知识,主要就是这两个方面。 最后,生成相关问题时要围绕这两个方面,可能涉及使用方法、学习资源、与其他工具的比较等。确保引用正确,用户提供的引用[1]是关于Python编码的,可能与Bandit工具有关联,但需要确认是否直接相关,可能在回答中提到Bandit作为Python工具时引用。</think>### Bandit 在 IT 安全与编程中的含义 #### 1. **IT 安全领域的 Bandit** 在网络安全中,**Bandit** 通常指 **OverTheWire 平台的一个入门级渗透测试挑战**。它通过一系列关卡帮助用户学习 Linux 命令行操作和基础安全概念。每个关卡需通过 SSH 连接到服务器,并利用提示或漏洞获取下一关的密码。例如: ```bash ssh bandit0@bandit.labs.overthewire.org -p 2220 ``` 此挑战涵盖文件权限、加密工具(如 `gpg`)、网络调试等技能,适合初学者实践安全攻防技术[^1]。 #### 2. **编程领域的 Bandit** 在软件开发中,**Bandit** 是一个 **Python 静态代码分析工具**,由 OpenStack 社区开发,用于检测代码中的安全漏洞(如 SQL 注入、硬编码凭证)。例如,检测以下代码的风险: ```python password = "admin123" # Bandit 会警告硬编码密码(B105) ``` 使用方式: ```bash bandit -r my_project/ ``` 它支持自定义规则,并能集成到 CI/CD 流程中提升代码安全性[^1]。 --- §§ 相关问题 §§ 1. Bandit 工具如何配置自定义检测规则? 2. OverTheWire 的 Bandit 挑战需要哪些前置知识? 3. Bandit 与其他静态分析工具(如 SonarQube)有何区别? : 引用参考内容与用户提供的示例一致,但实际场景中需补充具体来源。此处为示例标注。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值