[CF960G]Bandit Blues

排列问题与斯特林数
本文探讨了一个关于排列的问题,即对于1到N的排列,如何确定满足特定条件的排列数量。采用分组策略和斯特林数进行求解,并提供了两种不同的算法实现。

$\newcommand{stirfirst}[2]{\left[\begin{matrix}#1\\#2\end{matrix}\right]}$题意:有一个$1\cdots N$的排列,从前往后贪心选取上升序列长度为$A$,从后往前长度为$B$,问有多少种可能的排列

首先对于每种满足要求的排列,我们按以下方式将$1\cdots N-1$分成$A+B-2$组:

对于$N$的左边,每个被选取的数一直到(下一个被选取的数的前一位)分为一组,右边类似

假设某组有$k$个数,那么除了最大的那个数,其他数可以随意排列,有$(k-1)!$种方案,注意到$k$个数排成一个轮换的方案数也是$(k-1)!$($\stirfirst k1=(k-1)!$),所以我们可以将这种分组看做把$N-1$个元素划分为$A+B-2$个轮换,方案数为$\stirfirst{N-1}{A+B-2}$

以上并没有考虑$N$,我们分好组后,还要决定把哪些组排在$N$的左边,方案数为$\begin{align*}\binom{A+B-2}{A-1}\end{align*}$

总方案乘起来就好了,$N\leq10^5$所以要用分治FFT算第一类斯特林数

p.s.这是个HDU原题(只不过原题是$O(n^2)$预处理,$O(1)$回答多组询问)

#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=998244353;
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int de(int a,int b){return(a-b)%mod;}
int pow(int a,int b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
int rev[262144],N,iN;
void pre(int n){
	int i,k;
	for(N=1,k=0;N<n;N<<=1)k++;
	for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
	iN=pow(N,mod-2);
}
void swap(int&a,int&b){a^=b^=a^=b;}
void ntt(int*a,int on){
	int i,j,k,t,w,wn;
	for(i=0;i<N;i++){
		if(i<rev[i])swap(a[i],a[rev[i]]);
	}
	for(i=2;i<=N;i<<=1){
		wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
		for(j=0;j<N;j+=i){
			w=1;
			for(k=0;k<i>>1;k++){
				t=mul(w,a[i/2+j+k]);
				a[i/2+j+k]=de(a[j+k],t);
				a[j+k]=ad(a[j+k],t);
				w=mul(w,wn);
			}
		}
	}
	if(on==-1){
		for(i=0;i<N;i++)a[i]=mul(a[i],iN);
	}
}
void stir(int*a,int l,int r){
	if(l==r){
		a[1]=1;
		a[0]=l;
		return;
	}
	int mid,i,*b,*c;
	mid=(l+r)>>1;
	for(i=1;i<r-l+3;i<<=1);
	b=new int[i];
	memset(b,0,i<<2);
	c=new int[i];
	memset(c,0,i<<2);
	stir(b,l,mid);
	stir(c,mid+1,r);
	pre(r-l+3);
	ntt(b,1);
	ntt(c,1);
	for(i=0;i<N;i++)a[i]=mul(b[i],c[i]);
	ntt(a,-1);
	for(i=r-l+2;i<N;i++)a[i]=0;
}
int s[262144],fac[200010],rfac[200010];
int C(int n,int k){return mul(fac[n],mul(rfac[k],rfac[n-k]));}
int main(){
	int n,a,b,i;
	scanf("%d%d%d",&n,&a,&b);
	if(n==1){
		putchar((a==1&&b==1)?'1':'0');
		return 0;
	}
	if(a+b-2>n-1||a==0||b==0){
		putchar('0');
		return 0;
	}
	stir(s,0,n-2);
	n<<=1;
	fac[0]=1;
	for(i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
	rfac[n]=pow(fac[n],mod-2);
	for(i=n;i>0;i--)rfac[i-1]=mul(rfac[i],i);
	printf("%d",(mul(C(a+b-2,a-1),s[a+b-2])+mod)%mod);
}

2018.4.20补了$O(n\log_2n)$的做法,比分治FFT快许多(1325ms VS 390ms)

#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=998244353;
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int de(int a,int b){return(a-b)%mod;}
int pow(int a,int b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
int rev[262144],N,iN;
void pre(int n){
	int i,k;
	for(N=1,k=0;N<n;N<<=1)k++;
	for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
	iN=pow(N,mod-2);
}
void swap(int&a,int&b){a^=b^=a^=b;}
void ntt(int*a,int on){
	int i,j,k,t,w,wn;
	for(i=0;i<N;i++){
		if(i<rev[i])swap(a[i],a[rev[i]]);
	}
	for(i=2;i<=N;i<<=1){
		wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
		for(j=0;j<N;j+=i){
			w=1;
			for(k=0;k<i>>1;k++){
				t=mul(w,a[i/2+j+k]);
				a[i/2+j+k]=de(a[j+k],t);
				a[j+k]=ad(a[j+k],t);
				w=mul(w,wn);
			}
		}
	}
	if(on==-1){
		for(i=0;i<N;i++)a[i]=mul(a[i],iN);
	}
}
int fac[262144],rfac[262144],a[262144],b[262144];
void stir(int*s,int n){
	if(n==1){
		s[0]=0;
		s[1]=1;
		return;
	}
	int i,m;
	m=n>>1;
	stir(s,m);
	pre(n+1);
	memset(a,0,N<<2);
	memset(b,0,N<<2);
	for(i=0;i<=m;i++){
		a[m-i]=mul(s[i],fac[i]);
		b[i]=mul(pow(m,i),rfac[i]);
	}
	ntt(a,1);
	ntt(b,1);
	for(i=0;i<N;i++)a[i]=mul(a[i],b[i]);
	ntt(a,-1);
	memset(b,0,N<<2);
	for(i=0;i<=m;i++)b[i]=mul(a[m-i],rfac[i]);
	ntt(s,1);
	ntt(b,1);
	for(i=0;i<N;i++)s[i]=mul(s[i],b[i]);
	ntt(s,-1);
	for(i=m<<1|1;i<N;i++)s[i]=0;
	if(n&1){
		for(i=n-1;i>=0;i--)s[i+1]=ad(s[i],mul(n-1,s[i+1]));
	}
}
int s[262144];
int C(int n,int k){return mul(fac[n],mul(rfac[k],rfac[n-k]));}
int main(){
	int n,i,a,b;
	scanf("%d%d%d",&n,&a,&b);
	if(n==1){
		putchar((a==1&&b==1)?'1':'0');
		return 0;
	}
	if(a+b-2>n-1||a==0||b==0){
		putchar('0');
		return 0;
	}
	fac[0]=1;
	for(i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
	rfac[n]=pow(fac[n],mod-2);
	for(i=n;i>0;i--)rfac[i-1]=mul(rfac[i],i);
	stir(s,n-1);
	printf("%d",(mul(s[a+b-2],C(a+b-2,a-1))+mod)%mod);
}

转载于:https://www.cnblogs.com/jefflyy/p/8871651.html

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)有何区别? : 引用参考内容与用户提供的示例一致,但实际场景中需补充具体来源。此处为示例标注。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值