Description
有两个长度为n的排列A和B,定义排列的价值f(A,B)为所有满足A[i]>B[i]的位置i的数量。
现给出n,A,B和S,其中A和B中有一些位置的数未知,问有多少种可能的填数的方案使得f(A,B)=S
Data Constraint
对于20%的数据满足,1<=n<=10
对于50%的数据满足,1<=n<=20
对于70%的数据满足,1<=n<=200
对于100%的数据满足,1<=S<=n<=4000
保证不存在一个位置i满足A[i]=0且B[i]=0
Solution
显然A[i]=0的项和b[i]=0的项可以分开处理,a[i],b[i]都不为0的显然可以去掉。问题转化成在一个空的序列上放一些数,使它A[I]>b[i]的数量为k。这种问题很常见,但我现在才会做。我们设f[i][j]表示前i个A数组中我们选择j个使它>b[i],那么f[i][j]=f[i-1][j]+f[i-1][j-1](d[i]-j),d[i]表示比A的第i个数小的B的个数。由于我们只规定了必须有贡献的j个,剩下的随意分配,所以要f[n][i](n-i)!,但这样算的结果不是恰好贡献为j,而是至少贡献为j的方案,我们下面称为f[j]。我们设g[j]表示贡献恰好为j的方案。我们对f[y]的定义仔细研究后发现一个g[x]在f[y] (x>y)中计算
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=4e3+5,mo=1e9+7;
ll f[maxn][maxn],a[maxn],b[maxn],c[maxn],d[maxn],bz[maxn],g[maxn],p[maxn],ni[maxn],q[maxn],h[maxn];
ll n,m,i,t,j,k,l,x,y,z,ans;
ll mi(ll x,ll y){
if (y==1) return x;
t=mi(x,y/2);
if (y%2) return t*t%mo*x%mo;return t*t%mo;
}
ll pan(ll x,ll y){
return p[x]*ni[y]%mo*ni[x-y]%mo;
}
void make(int n){
f[0][0]=1;
for (i=1;i<=n;i++)
for (j=0;j<=i;j++)
f[i][j]=((j<i)*f[i-1][j]+(j!=0 && d[i]>=j)*f[i-1][j-1]*(d[i]-j+1)%mo)%mo;
for (i=n;i>=0;i--){
g[i]=f[n][i]*p[n-i]%mo;
for (j=i+1;j<=n;j++)
g[i]=(g[i]-pan(j,i)*g[j]%mo+mo)%mo;
}
}
int main(){
freopen("arrange.in","r",stdin);freopen("arrange.out","w",stdout);
scanf("%lld%lld",&n,&m);p[0]=1;
for (i=1;i<=n;i++)p[i]=p[i-1]*i%mo;
ni[n]=mi(p[n],mo-2);
for (i=n;i>=1;i--) ni[i-1]=ni[i]*i%mo;
for (i=1;i<=n;i++)scanf("%lld",&a[i]),bz[a[i]]++;
for (i=1;i<=n;i++)scanf("%lld",&b[i]),m-=(a[i]>b[i] && b[i]);
for (i=1;i<=n;i++)if (!a[i]) c[++c[0]]=b[i];j=0;
sort(c+1,c+c[0]+1);
for (i=1;i<=n;i++)if (!bz[i]){
while (i>c[j+1] && j<c[0]) j++;
d[++d[0]]=j;
}
make(d[0]);swap(q,g);
c[0]=d[0]=0;memset(bz,0,sizeof(bz));
for (i=1;i<=n;i++){
bz[b[i]]++;
if (!b[i]) h[++h[0]]=a[i];
}
for (i=1;i<=n;i++) if (!bz[i]) c[++c[0]]=i;j=0;
sort(h+1,h+h[0]+1);
for (i=1;i<=h[0];i++){
while (h[i]>c[j+1] && j<c[0]) j++;
d[i]=j;
}d[0]=h[0];
make(d[0]);
for (i=0;i<=m;i++)
ans=(ans+q[i]*g[m-i]%mo)%mo;
printf("%lld\n",ans);
}