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;
}

猜你喜欢

转载自www.cnblogs.com/lokiii/p/8949515.html