[abc309 G] Ban Permutation

发布时间 2023-07-16 20:15:27作者: LuoyuSitfitw

G - Ban Permutation

首先看到绝对值,很烦,考虑取掉绝对值得到\(p_i\leq i-X\)\(p_i\geq i+X\)

然后我们就自然而然有了一个暴力的想法,设\(dp[i][s]\)表示前\(i\)个数选了的数的状态为\(S\),然后空间复杂度是\(O(N2^N)\)的,时间复杂度也是\(O(N2^N)\)

这也太暴力了8,考虑优化

发现\(s\)这一维无论如何也优化不了,考虑减小它的状态数,我们先将\(dp[i][s]\)重新定义前\(i\)个的\(1\sim i-X\)\(i+x\sim N\)的选取状态,然后这样\(2^N\)就减小为了\(2^{N-2X+1}\)

然后我们又发现\(X\leq5\),非常的小!所以我们考虑将\(dp[i][s]\)再次重新定义为前\(i\)个的\(i-X+1\sim i+X-1\)的选取状态,发现这样,我们似乎就只能转移得到不合法的状态,但是这样我们的时空复杂度都是\(O(N2^{2X-1})\)的,可以过

所以我们考虑容斥,\(ans=\sum_{i=0}^n (n-i)!\times val(i)\)\(i\)表示我们钦定了\(i\)个不合法的数

然后考虑\(val(i)\)怎么整,显然我们只需要给\(dp[i][s]\)再加上一维变成\(dp[i][j][s]\)表示前\(i\)个,有\(j\)个不合法的,目前\(i-X+1\sim i+X-1\)的选取状态

那么\(val(i)\)就等于\(\sum_{s=0}^{2^{2X-1}}dp[n][i][s]\)

然后就可以完结撒花了 *★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

#include<bits/stdc++.h>
using namespace std;
const int N=105,MOD=998244353;
int n,x,dp[N][N][1<<9],lim,ans,jc[N],val[N];
void add(int &x,int y){ x+=y; if(x>=MOD) x-=MOD; if(x<0) x+=MOD; }
int ad(int x,int y){ x+=y; if(x>=MOD) x-=MOD; return x; }
void Init(){
	lim=(1<<((x<<1)-1))-1;
	jc[0]=1;
	for(int i=1;i<=n;++i) jc[i]=1ll*jc[i-1]*i%MOD;
}
int main(){
	scanf("%d%d",&n,&x),Init();
	dp[0][0][0]=1;
	for(int i=0;i<n;++i)
		for(int j=0;j<=i;++j)
			for(int s=0;s<=lim;++s){
				int now=s>>1;
				add(dp[i+1][j][now],dp[i][j][s]);
				int up=min((i+1)+x-1,n)-((i+1)-x+1),down=max(1,(i+1)-x+1)-((i+1)-x+1);
				for(int k=down;k<=up;++k) if(!(now>>k&1)) add(dp[i+1][j+1][now|(1<<k)],dp[i][j][s]);
			}
	for(int i=0;i<=n;++i) for(int s=0;s<=lim;++s) add(val[i],dp[n][i][s]);
	for(int i=0;i<=n;++i) add(ans,((i&1)?-1ll:1ll)*jc[n-i]%MOD*val[i]%MOD);
	printf("%d",ans);

	return 0;
}