bzoj 2087: [Poi2010]Sheep【凸包+极角排序+dp】

首先处理处理出来哪些边能连——能把羊分成两个偶数部分的,实现是在凸包上枚举极点,极角排序,枚举凸包上点对判断两边羊的个数的奇偶即可,设可以连边为v[i][j]=1
然后设f[i][j]为从i到j个凸包上点的方案数,初始状态是相邻点f[i][i+1]=1,转移是

\[f[i][j]=\sum_{k=i+1}^{i-1}[v[i][k]\&\&v[k][j]]f[i][k]*f[k][j] \]

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=605,M=20005;
int n,m,mod,f[N][N];
bool v[N][N];
struct dian
{
	int x,y;
	dian(int X=0,int Y=0)
	{
		x=X,y=Y;
	}
	dian operator - (const dian &a) const
	{
		return dian(x-a.x,y-a.y);
	}
	int operator * (const dian &a) const
	{
		return x*a.y-y*a.x;
	}
}a[N],b[M],d;
int read()
{
	int r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
inline bool cmp(const dian &a,const dian &b)
{
	return (a-d)*(b-d)<0;
}
int main()
{
	n=read(),m=read(),mod=read();
	for(int i=1;i<=n;i++)
		a[i].x=read(),a[i].y=read();
	for(int i=1;i<=m;i++)
		b[i].x=read(),b[i].y=read();
	for(int i=1;i<=n;++i)
	{
		d=a[i];
        sort(b+1,b+m+1,cmp);
		int x=0;
		for(int j=i+1;j<=n;++j)
		{
			while(x<m&&(b[x+1]-d)*(a[j]-d)<0)
				++x;
			if((x&1)||(b[x+1]-d)*(a[j]-d)==0)
				continue;
			v[i][j]=1;
		}
    }
	for(int i=1;i<=n;i++)
		f[i][i%n+1]=1;
	for(int l=2;l<=n;l++)
		for(int i=1,j;i<=n;i++)
		{
			j=(i+l-1)%n+1;
			if(v[i][j])
				for(int k=i%n+1;k!=j;k=k%n+1)
					if(v[i][k]&&v[k][j])
						f[i][j]=(f[i][j]+f[i][k]*f[k][j])%mod;
		}
	printf("%d\n",f[1][n]);
	return 0;
}
posted @ 2018-04-26 09:26  lokiii  阅读(237)  评论(0编辑  收藏  举报