【题解】有限制的排列 [51nod1296]
【题解】有限制的排列 [51nod1296]
【题目描述】
给出 \(n,m_1,m_2\) 和 \(\{a[1],a[2]...a[m_1]\},\{b[1],b[2]...b[m_2]\}\)
计算整数 \([1,n]\) 满足以下条件的排列个数:
位置 \(a[i]\) \((i \in [1,m_1])\) 上的数均小于其邻居,位置 \(b[i]\) \((i \in [1,m_2])\) 上的数均大于其邻居(位置编号从 \(0\) 开始)。
答案对 \(1e9+7\) 取模。
如样例:
\(N=4,a=\{1\},b=\{2\}\),符合条件的排列有:
\(2,1,4,3\)
\(3,2,4,1\)
\(4,2,3,1\)
\(3,1,4,2\)
\(4,1,3,2\)
答案为 \(5\) 。
【样例】
样例输入:
4 1 1
1
2
样例输出:
5
【数据范围】
\(100\%\) \(1 \leqslant n \leqslant 5000,\) \(1 \leqslant m_1,m_2 \leqslant n\)
【分析】
一道 \(dp\) 题,要维护的关键信息在于相邻数字的大小关系,用 \(dp[i][j]\) 表示整数 \([1,i]\) 满足要求的排列中,最后一个数选 \(a[i]=j\) 的排列数量。
为了方便状态的转移,只记录每个数和前一个数相比需要满足的大小关系。
开一个数组 \(p\):
\(p[i]=0\) : \(a[i]\) 无限制,\(dp[i][j]=\sum_{k=1}^{i-1} dp[i-1][k]\)
\(p[i]=1\) : \(a[i]\) 要小于 \(a[i-1]\),\(dp[i][j]=\sum_{k=j}^{i-1} dp[i-1][k]\)
\(p[i]=2\) : \(a[i]\) 要大于 \(a[i-1]\),\(dp[i][j]=\sum_{k=1}^{j-1} dp[i-1][k]\)
通常的理解是直接在已选好的序列后面加入了一个 \(j\),但并不能保证 \(j\) 在前面一定没有出现过,所以要理解为:把前面已选好的序列中大于等于 \(j\) 的数都加一后再把 \(j\) 加到后面。
所以当 \(p[i]=1\) 时,\(k\) 的下边界是 \(j\) 而不是 \(j+1\)(单纯的理解为结尾比 \(j\) 大的情况),因为当 \(a[i-1]\) 为 \(j\) 时,加了 \(1\) 之后就大于 \(j\) 了,此时 \(a[i]\) 恰好可为 \(j\) 。
而当 \(p[i]=2\) 时,\(k\) 的上边界却不能取到 \(j\),如果取到了 \(j\),那么 \(j+1\) 就会大于 \(j\),即 \(a[i-1] > a[i]\),该种排列与限制关系矛盾。
求和的过程可以用前缀和省略掉,初始化为 \(dp[1][1]=S[1]\) (\(dp[1]=1\) 为的是防止出现 \(n=1\) 这样的奇葩数据)。
时间复杂度:\(O(n^2)\) 。
【Code】
#include<algorithm>
#include<cstdio>
#include<queue>
#define Re register int
const int N=5003,P=1e9+7;
int x,n,m1,m2,ans,p[N],S[N],dp[N][N];
//p[i]=0 无限制
//p[i]=1 a[i-1]>a[i]
//p[i]=2 a[i-1]<a[i]
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
int main(){
in(n),in(m1),in(m2);
while(m1--)in(x),++x,p[x]=1,p[x+1]=2;
while(m2--)in(x),++x,p[x]=2,p[x+1]=1;
dp[1][1]=S[1]=1;
for(Re i=2;i<=n;++i){
for(Re j=1;j<=i;++j){
if(!p[i])(dp[i][j]+=S[i-1])%=P;
else if(p[i]<2)(dp[i][j]+=(S[i-1]-S[j-1]+P)%P)%=P;
else (dp[i][j]+=S[j-1])%=P;
}
for(Re j=1;j<=i;++j)S[j]=(S[j-1]+dp[i][j])%P;
}
for(Re i=1;i<=n;++i)(ans+=dp[n][i])%=P;
printf("%d",ans);
}