Description
如果假设一个藏羚羊群体有N只羊,就可以把它们的领地当做一个N*N的方阵,在这个方阵上第I列的第I行都有一个圣地,它们不会居住在圣地,同时每行每列只能居住一只羚羊。于是他们很快算出一个有N只羊的藏羚羊群体的居住地分布方法数。
Analysis
如果不考虑圣地的因素干扰,这题很容易写出动规方程:
dp[i]=dp[i-1]*i
什么意思呢?就是对于n·n的方阵由(n-1)·(n-1)转移而来,原先的行和列都已经占满,所以第n只绵羊只能放在新增的列和行上,新增的列当然是第n列,而新增的行可以放在n·n方阵的任意一行,共n种情况。
然而 圣地的存在限制了绵羊的放置,对于n·n方阵,显然的我们只需要考虑第n列绵羊的防治,它只能放在1~n-1行,放置之后,将原方阵切割,再合并之后可以看做(n-1)·(n-1)矩形。
4·4原图:
放在第四列第一行后:
舍去无法放置的区域后:
放在第四列第二行后可以画出:
等效替换后,发现他们都可以表示为(n-1)·(n-1)方阵下最后一行无圣地的情况,令无圣地情况为dp[n-1][1],那么dp[n][0]=dp[n-1][1]·(n-1)。
而此特殊情况如何计算呢?显然对于前n-1行处理方式与普通情况是一样的,只是最后一行又多了dp[i-1][0]种情况。
dp[i][0]=dp[i-1][1]*(n-1)
dp[i][1]=dp[i-1][0]+dp[i][0]
因为数据太大,需要使用高精度。
Code
#include <bits/stdc++.h>
struct bigint{
int len,num[5010];
bigint operator = (int eq){
len=0;
memset(num,0,sizeof(num));
while(eq){
num[len++]=eq%10;
eq/=10;
}
return *this;
}
bigint operator = (bigint eq){
len=eq.len;
memset(num,0,sizeof(num));
for(int i=0;i<len;i++)
num[i]=eq.num[i];
return *this;
}
bigint operator + (bigint ad){
bigint ans;
ans.len=std::max(len,ad.len);
memset(ans.num,0,sizeof(ans.num));
int add=0;
for(int i=0;i<ans.len;i++){
int res=num[i]+ad.num[i]+add;
ans.num[i]=res%10;
add=res/10;
}
if(add)ans.num[ans.len++]=add;
return ans;
}
bigint operator * (int mt){
bigint ans;
ans.len=len;
memset(ans.num,0,sizeof(ans.num));
for(int i=0;i<len;i++)
ans.num[i]=num[i]*mt;
int add=0;
for(int i=0;i<len;i++){
ans.num[i]+=add;
add=ans.num[i]/10;
ans.num[i]%=10;
}
while(add){
ans.num[ans.len++]=add%10;
add/=10;
}
return ans;
}
friend std::ostream& operator << (std::ostream& out,bigint ans){
for(int i=ans.len-1;i+1;i--)
out<<ans.num[i];
return out;
}
};
int n;
bigint dp[1010][2];
int main(){
freopen("keke.in","r",stdin);
freopen("keke.out","w",stdout);
std::cin>>n;
dp[1][0]=0;
dp[1][1]=1;
for(int i=2;i<=n;i++)
dp[i][0]=dp[i-1][1]*(i-1),dp[i][1]=dp[i][0]+dp[i-1][0];
std::cout<<dp[n][0]<<std::endl;
return 0;
}