1. 程式人生 > >【bzoj3630】[JLOI2014]鏡面通道 對偶圖+計算幾何+網絡流最小割

【bzoj3630】[JLOI2014]鏡面通道 對偶圖+計算幾何+網絡流最小割

log ros font eof ext 理學 最小 bool type

題目描述

在一個二維平面上,有一個鏡面通道,由鏡面AC,BD組成,AC,BD長度相等,且都平行於x軸,B位於(0,0)。通道中有n個外表面為鏡面的光學元件,光學元件α為圓形,光學元件β為矩形(這些元件可以與其他元件和通道有交集,具體看下圖)。光線可以在AB上任一點以任意角度射入通道,光線不會發生削弱。當出現元件與元件,元件和通道剛好接觸的情況視為光線無法透過(比如兩圓相切)。現在給出通道中所有元件的信息(α元件包括圓心坐標和半徑xi,yi,ri,β元件包括左下角和右上角坐標x1,y1,x2,y2)

如上圖,S到T便是一條合法線路。

當然,顯然存在光線無法透過的情況,現在交給你一個艱巨的任務,請求出至少拿走多少個光學元件後,存在一條光線線路可以從CD射出。

下面舉例說明:

現在假設,取走中間那個矩形,那麽就可以構造出一條穿過通道的光路,如圖中的S到T。

輸入

第一行包含兩個整數,x,y,表示C點坐標

第二行包含一個數字,n,表示有n個光學元件

接下來n行

第一個數字如果是1,表示元件α,後面會有三個整數xi,yi,ri分別表示圓心坐標和半徑

第一個數字如果是2,表示元件β,後面會有四個整數x1,y1,x2,y2分別表示左下角和右上角坐標(矩形都平行,垂直於坐標軸)

輸出

輸出包含一行,至少需要拿走的光學元件個數m

樣例輸入

1000 100
6
1 500 0 50
2 10 10 20 100
2 100 10 200 100
2 300 10 400 100

2 500 10 600 100
2 700 0 800 100

樣例輸出

2


題解

對偶圖+計算幾何+網絡流最小割

首先有個神奇的物理學結論:水能通過的地方光也一定能通過。

因此直接判定左右通道是否連通即可。而和 bzoj3007 類似,左右連通意味著對偶圖上下不連通。

所以問題轉化為去掉最少的點使得上下不連通。這顯然是個最小割問題。

拆點,如果兩個元件相交,則互相連出點->入點的邊,容量為inf;每個點的入點->出點,容量為1。最小割即為答案。

不過有點惡心的是原件相交的判定,需要耐心= =

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 610
#define M 400010
using namespace std;
typedef long long ll;
const int inf = 1 << 30;
queue<int> q;
int flag[N] , a[N] , b[N] , c[N] , d[N] , head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N];
void add(int x , int y , int z)
{
    to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
    int x , i;
    memset(dis , 0 , sizeof(dis));
    while(!q.empty()) q.pop();
    dis[s] = 1 , q.push(s);
    while(!q.empty())
    {
        x = q.front() , q.pop();
        for(i = head[x] ; i ; i = next[i])
        {
            if(val[i] && !dis[to[i]])
            {
                dis[to[i]] = dis[x] + 1;
                if(to[i] == t) return 1;
                q.push(to[i]);
            }
        }
    }
    return 0;
}
int dinic(int x , int low)
{
    if(x == t) return low;
    int temp = low , i , k;
    for(i = head[x] ; i ; i = next[i])
    {
        if(val[i] && dis[to[i]] == dis[x] + 1)
        {
            k = dinic(to[i] , min(temp , val[i]));
            if(!k) dis[to[i]] = 0;
            val[i] -= k , val[i ^ 1] += k;
            if(!(temp -= k)) break;
        }
    }
    return low - temp;
}
ll calc(int x , int y)
{
    return (ll)x * x + (ll)y * y;
}
bool judge(int x , int y)
{
    if(flag[x] + flag[y] == 2) return calc(a[x] - a[y] , b[x] - b[y]) <= (ll)(c[x] + c[y]) * (c[x] + c[y]);
    else if(flag[x] + flag[y] == 3)
    {
        if(flag[x] == 2) swap(x , y);
        if(calc(a[x] - a[y] , b[x] - b[y]) <= (ll)c[x] * c[x]) return 1;
        if(calc(a[x] - a[y] , b[x] - d[y]) <= (ll)c[x] * c[x]) return 1;
        if(calc(a[x] - c[y] , b[x] - b[y]) <= (ll)c[x] * c[x]) return 1;
        if(calc(a[x] - c[y] , b[x] - d[y]) <= (ll)c[x] * c[x]) return 1;
        if(abs(a[x] - a[y]) <= c[x] && b[x] >= b[y] && b[x] <= d[y]) return 1;
        if(abs(a[x] - c[y]) <= c[x] && b[x] >= b[y] && b[x] <= d[y]) return 1;
        if(abs(b[x] - b[y]) <= c[x] && a[x] >= a[y] && a[x] <= c[y]) return 1;
        if(abs(b[x] - d[y]) <= c[x] && a[x] >= a[y] && a[x] <= c[y]) return 1;
        return 0;
    }
    else
    {
        if(a[x] >= a[y] && a[x] <= c[y] && b[y] >= b[x] && b[y] <= d[x]) return 1;
        if(a[x] >= a[y] && a[x] <= c[y] && d[y] >= b[x] && d[y] <= d[x]) return 1;
        if(b[x] >= b[y] && b[x] <= d[y] && a[y] >= a[x] && a[y] <= c[x]) return 1;
        if(b[x] >= b[y] && b[x] <= d[y] && c[y] >= a[x] && c[y] <= c[x]) return 1;
        if(c[x] >= a[y] && c[x] <= c[y] && b[y] >= b[x] && b[y] <= d[x]) return 1;
        if(c[x] >= a[y] && c[x] <= c[y] && d[y] >= b[x] && d[y] <= d[x]) return 1;
        if(d[x] >= b[y] && d[x] <= d[y] && a[y] >= a[x] && a[y] <= c[x]) return 1;
        if(d[x] >= b[y] && d[x] <= d[y] && c[y] >= a[x] && c[y] <= c[x]) return 1;
        return 0;
    }
    return 0;
}
int main()
{
    int h , n , i , j , ans = 0;
    scanf("%*d%d%d" , &h , &n) , s = 0 , t = 2 * n + 2;
    for(i = 1 ; i <= n ; i ++ )
    {
        scanf("%d%d%d%d" , &flag[i] , &a[i] , &b[i] , &c[i]);
        add(i + n + 1 , i , 1);
        if(flag[i] == 1)
        {
            if(b[i] + c[i] >= h) add(s , i + n + 1 , inf);
            if(b[i] - c[i] <= 0) add(i , t , inf);
        }
        if(flag[i] == 2)
        {
            scanf("%d" , &d[i]);
            if(d[i] >= h) add(s , i + n + 1 , inf);
            if(b[i] <= 0) add(i , t , inf);
        }
        for(j = 1 ; j < i ; j ++ )
            if(judge(i , j))
                add(i , j + n + 1 , inf) , add(j , i + n + 1 , inf);
    }
    while(bfs()) ans += dinic(s , inf);
    printf("%d\n" , ans);
    return 0;
}

【bzoj3630】[JLOI2014]鏡面通道 對偶圖+計算幾何+網絡流最小割