bzoj4563放棋子
每一行只有一个障碍,每一列也只有一个障碍
可以把某些行交换一下,得到类似下面的格式
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
交换之后并不影响方案数
然后,把每一列压缩成一个位置,问题转化为每一个位置放一个棋子且不能放在原来的位置(第i个个棋子原来的位置是i)
转化为错排公式
#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 1000000000
#define LL long long
#define maxn 5005
using namespace std;
int n;
struct Bignum
{
LL s[maxn];
int len;
Bignum(){ memset(s,0,sizeof(s)); len=0; }
}A,B,C;
Bignum operator + (const Bignum a,const Bignum b)
{
Bignum c;
int i=1;
LL x=0;
while(i<=a.len||i<=b.len)
{
c.s[i]=x+a.s[i]+b.s[i];
x=c.s[i]/mod;
c.s[i]%=mod;
i++;
}
c.len=max(a.len,b.len);
while(x){ c.s[++c.len]=x%mod; x/=mod;}
while(c.len&&!c.s[c.len]) c.len--;
return c;
}
Bignum operator *(const Bignum a,int b)
{
Bignum c;
c.len=a.len;
LL x=0;
for(int i=1;i<=a.len;i++)
{
c.s[i]=a.s[i]*(LL)b+x;
x=c.s[i]/mod;
c.s[i]%=mod;
}
while(x){ c.s[++c.len]=x%mod; x/=mod; }
while(c.len&&!c.s[c.len]) c.len--;
return c;
}
void print(Bignum &a)
{
printf("%lld",a.s[a.len]);
for(int i=a.len-1;i>=1;i--)
printf("%09lld",a.s[i]);
printf("\n");
}
int main()
{
//freopen("chess_2016.in","r",stdin);
//freopen("chess_2016.out","w",stdout);
scanf("%d",&n);
int x;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) scanf("%d",&x);
if(n==1){ printf("0\n"); return 0; }
if(n==2){ printf("1\n"); return 0; }
A.len=1; A.s[1]=0;
B.len=1; B.s[1]=1;
for(int i=3;i<=n;i++)
{
C=(A+B)*(LL)(i-1);
A=B;
B=C;
}
print(C);
return 0;
}