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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章