切割多边形
Description
在一个凸n边形中,通过不相交于n边形内部的对角线,把n边形拆分成若干三角形。现在的任务是从键盘输入凸多边形的边数n,求不同剖分的方案数Cn。
Analysis
困难的一题。
不谈1000的数据量加上大整数会不会TLE,先分析一下转移方程。
不妨以点为突破口。显而易见,每个点要么被连接,要么不被连接。
- 当不被连接时,其相邻两点一定连接,则n边形转化为n-1边形。
- 当被连接时,可以与n-3个点相连,那么相连之后分割出i边形和n-i+2边形。为了防止重复,即连接第i个点时要保证不连接i+1n-3,可以将n-i+2边形转化为n-i+1边形,即自动视为此点不与i+1n-3个点相连。
动规方程:
dp[i]=dp[i-1]+dp[3]*dp[i-2]+...+dp[i-1]*dp[2]
因为dp[2]=1,所以又可以写成
dp[i]=dp[2]*dp[i-1]+...+dp[i-1]*dp[2]
状态转移复杂度为O(n^2),再算上高精度处理O(1000),TLE。
那么,怎么通过O(n)乃至更小的复杂度转移状态呢。我不知道,所以看了hj解题报告,了解了Catlan Queue。
不进行证明。
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 mt){
bigint ans;
ans.len=len+mt.len-1;
memset(ans.num,0,sizeof(ans.num));
for(int i=0;i<len;i++)
for(int j=0;j<mt.len;j++)
ans.num[i+j]+=num[i]*mt.num[j];
int add=0;
for(int i=0;i<ans.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;
}
bigint operator * (int mt){
bigint bigmt;
bigmt=mt;
return *this*bigmt;
}
bigint operator *= (int mt){
return *this=*this*mt;
}
bigint operator / (int dv){
bigint ans;
ans.len=len;
memset(ans.num,0,sizeof(ans.num));
int bc=0;
for(int i=len-1;i>=0;i--){
bc=bc*10+num[i];
if(bc>=dv)ans.num[i]=bc/dv,bc%=dv;
}
while(ans.len-1&&!ans.num[ans.len-1])ans.len--;
return ans;
}
bigint operator /= (int dv){
return *this=*this/dv;
}
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 Catlan(int x){
bigint ans;
ans=x+2;
for(int i=x+3;i<=x*2;i++)
ans*=i;
for(int i=2;i<=x;i++)
ans/=i;
return ans;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
std::cin>>n;
if(n==1)std::cout<<0<<std::endl;
else if(n<4)std::cout<<1<<std::endl;
else std::cout<<Catlan(n-2)<<std::endl;
return 0;
}