传送门
这个340E竟然是340e,让人觉得很诡异。。。
稍微分析一下就可以发现这题本质是求 个数排列,有 个数可以随便排,其余错排的方案数。
回忆一下,我们证明错排通项公式的时候是怎么容斥的,其实这题也差不多。
就是总方案数 - 1个在原来位置上的方案数 + 2个在原来位置上的方案数 - 3个在原来位置上的方案数……
具体来说,有
个在原来位置上的方案数,就是先在要错排的数里选
个放在原来的位置上,其余的数乱排。
写得好看一点就是
具体实现看代码吧,不过我的代码比较奇怪, 其实是
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2001;
const int p=1e9+7;
int n,a[N],s,q,b[N];
LL mul[N],P[N],C[N][N],ans;
void read(int &x){
char ch=getchar();x=0;int w=1;
for(;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
x*=w;
}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(a[i]);
if (a[i]==-1) s++;
else b[a[i]]=1;
}
for(int i=1;i<=n;i++) if (b[i]&&a[i]==-1) q++;
mul[0]=1;
for(int i=1;i<=n;i++) mul[i]=mul[i-1]*i%p;
/*P[0]=1;P[1]=0;
for(int i=2;i<=n;i++) P[i]=(n-1)*(P[i-1]+P[i-2])%p;*/
C[0][0]=1;
for(int i=1;i<=n+1;i++)
for(int j=1;j<=n+1;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
ans=mul[s];int w=1;
for(int i=1;i<=s-q;i++){
w*=-1;
ans=(ans+w*C[s-q+1][i+1]*mul[s-i]%p)%p;
}
cout<<(ans+p)%p;
return 0;
}