AGC030F

luogu鏈接

解法

考慮如果A中的兩個相鄰位置都確定了的話,這兩個位置所對應的B的位置不會對B的方案產生影響,直接不管就可以了。
如果建立圖論模型,可以發現B的每一個數相當於對A中的兩個數匹配後,較小的值
然後記錄每個數是否在A中出現過,然後可以從大到小dp:
f[i][j][k]f[i][j][k]表示考慮了i\ge i的所有位置,其中有jj個已經在A中出現的還需要向比i更小的位置匹配,有kk個沒有在A中出現的需要向比i更小的位置匹配。
轉移考慮從i+1i+1轉移過來:
iA討論i是否在A中出現過:
f[i][j][k]+=f[i+1][j1][k],i如果出現過:f[i][j][k]+=f[i+1][j-1][k],這裏的意思是i也要和更小的數匹配
f[i][j][k]+=f[i+1][j][k+1],iAiA如果出現過:f[i][j][k]+=f[i+1][j][k+1],這裏是i和沒有出現在A中的更大的數匹配。注意i不能和已經出現在A中的更大的數匹配,因爲如果要匹配的話,這兩個數必須相鄰,這種情況一開始已經考慮過了
,如果沒出現過,則可以和更小的數匹配,或者和任意更大的數匹配,但是如果匹配的是已經出現過的更大的數,因爲匹配任意一個都可以,所以要乘上個數,如果是未出現的,因爲所有未出現的可以互相替換順序,所以一開始算上一個所有未出現位置個數的階乘

#include<bits/stdc++.h>
using namespace std;
const int maxn=605;
const int mod=1e9+7;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,a[maxn],b[maxn],t,c[maxn];
int f[2][maxn][maxn];
inline void add(int &a,int b){
	a=a+b;
	if(a>=mod)a=a-mod;
	if(a<0)a=a+mod;
}
signed main(){
	n=read();
	for(int i=1;i<=2*n;i++)a[i]=read();
	int cnt=0;
	for(int i=1;i<=2*n;i+=2){
		if((a[i]!=-1)&&(a[i+1]!=-1))b[a[i]]=b[a[i+1]]=2;
		else if(a[i]!=-1){b[a[i]]=1;}
		else if(a[i+1]!=-1){b[a[i+1]]=1;}
		else cnt++;
	}
	for(int i=1;i<=2*n;i++){
		if(b[i]==1)c[++t]=1;
		else if(b[i]==0)c[++t]=2;
	}
	f[t&1][0][0]=1;
	for(int i=t;i>=1;i--){
		memset(f[(i-1)&1],0,sizeof(f[(i-1)&1]));
		for(int j=0;j<=t&&j<=n;j++){
			for(int k=0;k<=t&&k<=n;k++){
				if(c[i]==1){
					add(f[(i-1)&1][j+1][k],f[i&1][j][k]);
					if(k)add(f[(i-1)&1][j][k-1],f[i&1][j][k]);
				}
				else{
					add(f[(i-1)&1][j][k+1],f[i&1][j][k]);
					if(k)add(f[(i-1)&1][j][k-1],f[i&1][j][k]);
					if(j)add(f[(i-1)&1][j-1][k],(1ll*f[i&1][j][k]*j)%mod);
				}
			}
		}
	}
	int ans=f[0][0][0];
	for(int i=1;i<=cnt;i++)
	ans=1ll*ans*i%mod;
	printf("%d\n",ans);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章