Description
有两个长度为n的排列A和B,定义排列的价值f(A,B)为所有满足A[i]>B[i]的位置i的数量。
现给出n,A,B和S,其中A和B中有一些位置的数未知,问有多少种可能的填数的方案使得f(A,B)=S
Input
第一行两个整数n和S
第二行n个数表示排列A
第三行n个数表示排列B
其中A和B中某些位置是0表示当前位置上的数还未确定,保证不存在一个位置i满足A[i]=0且B[i]=0
Output
一行一个整数表示使得f(A,B)=S的填数的方案数,由于答案可能很大,对10^9+7取模。
Sample Input
4 2
4 2 0 0
0 0 4 2
Sample Output
2
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
比赛的时候看错题了,以为是个水题。。发现以后只会做50分,弱爆了。。
这题与bzoj3198有异曲同工之妙,都是组合数作为容斥系数。
首先可以发现,上或者下为0是相同的处理方法,所以这里只讨论a[i]=0,b[i]!=0的情况。
把这些列抽出来,存在u,v里面,然后进行DP。
可以发现,此时并不需要遵守同下标的要求,任意两个之间只要大于号成立皆可配对,于是我们使用组合数处理,但是由于会算重,所以进行容斥。
具体来说就是
dp[i][j]+=dp[i+1][j−1]∗(tot−x+1−j+1)
f[i]+=c∗(dp[1][j]∗C(j,i)∗fac[tot−j])
c是容斥系数。
分成两个部分处理即可。
那么最终答案就是
∑g[m−i]∗f[i]
%%%ymw。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=4e3+5;
int n,m,a[N],b[N],ans;
int f[N],g[N],a1[N],b1[N];
int dp[N][N],vis[N];
const int mo=1e9+7;
typedef long long ll;
ll fac[N],inv[N];
inline ll pow(ll a,ll b)
{
ll ret=1;
while (b)
{
if (b&1)ret=ret*a%mo;
a=a*a%mo;
b>>=1;
}
return ret;
}
inline int C(int n,int m)
{
return 1ll*fac[n]*inv[m]%mo*inv[n-m]%mo;
}
inline void solve1()
{
memset(vis,0,sizeof(vis));
int tot=0;
fo(i,1,n)vis[a[i]]=1;
fo(i,1,n)if (!vis[i])a1[++tot]=i;
tot=0;
fo(i,1,n)if (!a[i])b1[++tot]=b[i];
sort(b1+1,b1+tot+1);
fo(i,1,tot+1)dp[i][0]=1;
fo(j,1,tot)
{
int x=tot+1;
fd(i,tot,1)
{
dp[i][j]=dp[i+1][j];
while (x>1&&b1[i]<a1[x-1])x--;
if (tot-x+1>j-1)
dp[i][j]=
(dp[i][j]+1ll*dp[i+1][j-1]*(tot-x+1-j+1)%mo)%mo;
}
}
fo(i,0,tot)
{
fo(j,i,tot)
if ((j-i)%2==0)
{
f[i]=(f[i]+1ll*dp[1][j]*C(j,i)%mo*fac[tot-j]%mo)%mo;
}
else
{
f[i]=(f[i]-1ll*dp[1][j]*C(j,i)%mo*fac[tot-j]%mo)%mo;
if(f[i]<0)f[i]+=mo;
}
}
}
inline void solve2()
{
memset(vis,0,sizeof(vis));
int tot=0;
fo(i,1,n)vis[b[i]]=1;
fo(i,1,n)if (!vis[i])b1[++tot]=i;
tot=0;
fo(i,1,n)if (!b[i])a1[++tot]=a[i];
sort(a1+1,a1+tot+1);
memset(dp,0,sizeof(dp));
fo(i,1,tot+1)dp[i][0]=1;
fo(j,1,tot)
{
int x=tot+1;
fd(i,tot,1)
{
dp[i][j]=dp[i+1][j];
while (x>1&&b1[i]<a1[x-1])x--;
if (tot-x+1>j-1)
dp[i][j]=(dp[i][j]+1ll*dp[i+1][j-1]*(tot-x+1-j+1)%mo)%mo;
}
}
fo(i,0,tot)
{
fo(j,i,tot)
if ((j-i)%2==0)
{
g[i]=(g[i]+1ll*dp[1][j]*C(j,i)%mo*fac[tot-j]%mo)%mo;
}
else
{
g[i]=(g[i]-1ll*dp[1][j]*C(j,i)%mo*fac[tot-j]%mo)%mo;
if(f[i]<0)f[i]+=mo;
}
}
}
int main()
{
freopen("arrange.in","r",stdin);
freopen("arrange.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n)scanf("%d",&a[i]);
fo(i,1,n)scanf("%d",&b[i]);
fac[0]=inv[0]=1;
fo(i,1,n)fac[i]=1ll*fac[i-1]*i%mo,inv[i]=pow(fac[i],mo-2);
solve1();
solve2();
fo(i,1,n)if (a[i]&&b[i]&&a[i]>b[i])m--;
ll ans=0;
fo(i,0,m)ans=(ans+1ll*f[i]*g[m-i]%mo)%mo;
printf("%lld\n",ans%mo);
}