JZOJ 5429 排列

来源:互联网 发布:热传导计算软件 编辑:程序博客网 时间:2024/06/14 10:06

排列

Description

有两个长度为n的排列AB,定义排列的价值f(A,B)为所有满足Ai>Bi的位置i的数量。
现给出nABS,其中AB中有一些位置的数未知,问有多少种可能的填数的方案使得f(A,B)=S
若第i位上位置则为0
答案可能很大,对109+7取模。

Data Constraint

1<=S<=n<=4103
保证不存在一个位置i满足Ai=0Bi=0

Solution

很显然可以把问题拆成两个完全一样的问题。
a序列中缺的数取出来排好序,把b序列中失配的数取出来。
ki表示a序列中第i个数做多能够匹配到b序列中的第ki个数。
fi,j表示a序列中的数做到了第i位,其中有j对匹配造成了贡献。
易得fi,j=fi1,j+fi1,j1*(ki-j+1)
Gi表示整个a序列已处理完,保证有j的贡献的方案数(贡献至少为j)。
易得Gi=fn,i*(n-i)!
Fi表示贡献恰好为i的方案数。
通过容斥原理可得Gi=j>=iFj*Cij
移项可得Fi=Gi-j>iFj*Cij
这样子问题就解决了,最后答案的计算只需合并两个子问题的答案即可。

Code

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,j,l) for(int i=j;i<=l;i++)#define fd(i,j,l) for(int i=j;i>=l;i--)using namespace std;typedef long long ll;const ll N=45e2,mo=1e9+7;ll f1[N],g1[N][N],f2[N],g2[N][N],a[N],b[N],c[N],d[N],e[N],least[N];int n,m,j,k,l,i,o,p,u,v;ll jc[N],ny[N],r1[N],r2[N];ll ksm(ll o,ll t){    ll yy=1;    for(;t;t>>=1,o=o*o%mo)    if(t%2)yy=yy*o%mo;    return yy;}int min(int a,int b){if(a<b)return a;else return b;}ll C(ll a,ll b){    return (jc[a]*ny[b]%mo)*ny[a-b]%mo;}void work1(){    int k=0,v=0;    fo(i,1,n)if(!r1[i])e[++k]=i;    fo(i,1,k){        for(;e[i]>d[v+1]&&v<k;)v++;        least[i]=v;    }    fo(i,0,k)g1[i][0]=1;    fo(i,1,k)    fo(l,1,min(least[i],i))    g1[i][l]=(g1[i-1][l]+g1[i-1][l-1]*(least[i]-l+1))%mo;    f1[k]=g1[k][k];    fd(i,k-1,1){        f1[i]=g1[k][i]*jc[k-i]%mo;        fo(j,i+1,k)f1[i]=(f1[i]-f1[j]*C(j,i))%mo;    }    f1[0]=jc[k];    fo(i,1,k)f1[0]=(f1[0]-f1[i])%mo;}void work2(){    int k=0,v=0;    fd(i,n,1)if(!r2[i])e[++k]=i;    fo(i,1,k){        for(;e[i]<c[v+1]&&v<k;)v++;        least[i]=v;    }    fo(i,0,k)g2[i][0]=1;    fo(i,1,k)    fo(l,1,min(least[i],i))    g2[i][l]=(g2[i-1][l]+g2[i-1][l-1]*(least[i]-l+1))%mo;    f2[k]=g2[k][k];    fd(i,k-1,1){        f2[i]=g2[k][i]*jc[k-i]%mo;        fo(j,i+1,k)f2[i]=(f2[i]-f2[j]*C(j,i))%mo;    }    f2[0]=jc[k];    fo(i,1,k)f2[0]=(f2[0]-f2[i])%mo;}int main(){    cin>>n>>m;    jc[0]=ny[0]=1;    fo(i,1,n)jc[i]=jc[i-1]*i%mo,ny[i]=ksm(jc[i],mo-2);    fo(i,1,n)scanf("%d",&a[i]);    fo(i,1,n)scanf("%d",&b[i]);    fo(i,1,n){        if(!a[i])d[++u]=b[i]; r1[a[i]]++;        if(!b[i])c[++v]=a[i]; r2[b[i]]++;        if((a[i]*b[i]))m-=(a[i]>b[i]);    }    sort(d+1,d+u+1);    sort(c+1,c+v+1);    fo(i,1,v/2)swap(c[i],c[v-i+1]);    work1(); work2();    ll ans;    fo(i,1,m)    ans=(ans+f1[i]*f2[m-i])%mo;    ans=(ans+mo)%mo;    printf("%lld",ans);}
原创粉丝点击