AT_agc030_f [AGC030F] Permutation and Minimum 题解

先去掉相邻两个都填完的位置,对于两个都没填的记个数为 \(c\),最后只需要将答案乘上 \(c!\)

接下来考虑从小到大枚举所有数进行 dp,记 \(f_{i,j,k}\) 表示考虑完前 \(1\sim i\),有 \(j\) 个数需要跟一个位置确定的数匹配,有 \(k\) 个数需要跟后面一个自由的数匹配,考虑当前的数:

  • 如果已经确定位置,则有转移 \(f_{i-1,j,k}\to f_{i,j-1,k}\)\(f_{i-1,j,k}\to f_{i,j,k+1}\)
  • 如果没有确定位置,则有转移 \(f_{i-1,j,k}\to f_{i,j,k-1}\)\(f_{i-1,j,k}\cdot(j+1)\to f_{i,j+1,k}\)\(f_{i-1,j,k}\to f_{i,j,k+1}\)

直接转移即可,最后的答案即为 \(f_{2n,0,0}\cdot c!\),时间复杂度 \(\mathcal O(n^3)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define md 1000000007
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,ct,a[603],dp[603][303][303];
bool b[603],c[603];
ll ans;
signed main(){
	scanf("%d",&n);
	rep(i,1,n<<1)scanf("%d",&a[i]);
	for(int i=1;i<=n<<1;i+=2){
		if(a[i]==-1&&a[i+1]==-1)ct++;
		else if(a[i]==-1)b[a[i+1]]=1;
		else if(a[i+1]==-1)b[a[i]]=1;
		else c[a[i]]=c[a[i+1]]=1;
	}
	dp[0][0][0]=1;
	rep(i,1,n<<1){
		rep(j,0,n)rep(k,0,n)if(dp[i-1][j][k]){
			if(c[i]){
				dp[i][j][k]=dp[i-1][j][k];
				continue;
			}
			if(b[i]){
				if(j)dp[i][j-1][k]=(dp[i][j-1][k]+dp[i-1][j][k])%md;
				dp[i][j][k+1]=(dp[i][j][k+1]+dp[i-1][j][k])%md;
			}else{
				if(k)dp[i][j][k-1]=(dp[i][j][k-1]+dp[i-1][j][k])%md;
				dp[i][j+1][k]=(dp[i][j+1][k]+dp[i-1][j][k]*(j+1ll))%md;
				dp[i][j][k+1]=(dp[i][j][k+1]+dp[i-1][j][k])%md;
			}
		}
	}
	ans=dp[n<<1][0][0];
	rep(i,1,ct)ans=ans*i%md;
	cout<<ans;
	return 0;
}
posted @ 2024-12-19 16:07  zifanwang  阅读(3)  评论(0编辑  收藏  举报