BZOJ2087[Poi2010] Sheep

BZOJ2087[Poi2010] Sheep

Description

Lyx的QQ牧场养了很多偶数个的羊,他是Vip,所以牧场是凸多边形(畸形)。现在因为他开挂,受到了惩罚,系统要求他把牧场全部分为三角形(划分线不能在牧场中相交,只能在顶点相交),羊也是有个性的,如果他们在三角形中是单数就会有羊自杀(Lyx的样就是畸形),这让Lyx很难办,于是他向你求助了。

Input

输入:第一行由空格隔开的3个整数n(4<=n<=600),k(2<=k<=20000),m(2<=m<=20000),n表示牧场的顶点数,k表示羊的个数(保证为偶数)。接下来n行为顶点的座标xi、yi,(-15000<=xi,yi<=15000)由空格隔开,接下来k行为羊的座标,pi,pj,和xi,yi范围一样但是不会在顶点上,严格在牧场内。

Output

输出:牧场能划分的总方案数被m除的余数。

Sample Input

5 4 10

5 5

3 0

-1 -1

-3 4

1 10

1 0

-1 0

1 6

-2 5

Sample Output

3

img

Solution:

比较明显是dp吧。其实之前TC上有过一道类似的题,那个比较简单好吧

首先我们当我们连起一条线,我们可以把偶数之羊分成偶数与偶数,但无论如何也不能把奇数分成偶数与偶数,因此我们判断两点之间能不能连边,只需要判断两边的羊是否都是偶数只。

这里我们就需要将这些点按照一个比较舒服的方式排序(比如说顺时针或者是逆时针)。于是就用到了极角排序。其实看张图就知道这个排序是干什么的了:

img

当然这个排序可以使用反三角函数arctan 直接比较,但是精度略微堪忧,因此采用向量的叉积。

向量的叉积:

img
公式:res=a⃗ ×b⃗ =x1y2x2y1

  • res>0 时,表示a⃗ b⃗  的顺时针方向
  • res<0 时,表示a⃗ b⃗  的逆时针方向
  • res=0 时,表示a⃗ b⃗  共线

于是极角排序的代码就可以写出来了。调用sort(begin,end,cmp) 即可。(now作为基准点)

struct Point{
    int x,y;
    Point operator -(const Point &a)const{
        return (Point){x-a.x,y-a.y};
    }
    int operator *(const Point &a)const{
        return x*a.y-a.x*y;
    }
}A[M],now;
bool cmp(Point a,Point b){
    return (a-now)*(b-now)<0;
}

注意点:极角排序的点与基准点连线的最大角度不能超过180 ,因为它是基于向量的,也就是相当于说基准点要选在凸包上。

然后对于这道题,就可以用这种方法直接处理出哪两个点之间可以连边,dp方程就显而易见了。

dp[L][R]=i=L+1R1dp[L][i]dp[i][R](mp[L][i]mp[i][R])

于是就解决了这道题,复杂度O(nklogk)
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define M 605
#define N 20005
using namespace std;
struct Point{
    int x,y;
    Point operator -(const Point &a)const{
        return (Point){x-a.x,y-a.y};
    }
    int operator *(const Point &a)const{
        return x*a.y-a.x*y;
    }
}Q[M],K[N],now;
bool cmp(Point a,Point b){
    return (a-now)*(b-now)<0;
}
int mp[M][M],dp[M][M],m;
int dfs(int L,int R){
    int &res=dp[L][R];
    if(res!=-1)return res;
    res=0;
    for(int i=L+1;i<R;i++){
        if(mp[L][i]&&mp[i][R]){
            res=(res+1LL*dfs(L,i)*dfs(i,R))%m;
        }
    }
    return res;
}
int main(){
    int n,k;
    memset(dp,-1,sizeof(dp));
    scanf("%d %d %d",&n,&k,&m);
    for(int i=1;i<=n;i++)
        scanf("%d %d",&Q[i].x,&Q[i].y);
    for(int i=1;i<=k;i++)
        scanf("%d %d",&K[i].x,&K[i].y);
    now=Q[1];
    sort(Q+2,Q+n+1,cmp);
    for(int i=1;i<=n;i++){
        now=Q[i];
        sort(K+1,K+k+1,cmp);
        int res=0;
        for(int j=i+1;j<=n;j++){
            while(res<k&&(K[res+1]-now)*(Q[j]-now)<0)res++;
            if(res&1||(K[res+1]-now)*(Q[j]-now)==0)continue;
            mp[i][j]=mp[j][i]=true;
        }
    }
    for(int i=1;i<n;i++)
        dp[i][i+1]=1;
    printf("%d\n",dfs(1,n));
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章