BZOJ3630

3630: [JLOI2014]镜面通道

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 478  Solved: 166
[ Submit][ Status][ Discuss]

Description

在一个二维平面上,有一个镜面通道,由镜面AC,BD组成,AC,BD长度相等,且都平行于x轴,B位于(0,0)。通道中有n个外表面为镜面的光学元件,光学元件α为圆形,光学元件β为矩形(这些元件可以与其他元件和通道有交集,具体看下图)。光线可以在AB上任一点以任意角度射入通道,光线不会发生削弱。当出现元件与元件,元件和通道刚好接触的情况视为光线无法透过(比如两圆相切)。现在给出通道中所有元件的信息(α元件包括圆心坐标和半径xi,yi,ri,β元件包括左下角和右上角坐标x1,y1,x2,y2)

如上图,S到T便是一条合法线路。

当然,显然存在光线无法透过的情况,现在交给你一个艰巨的任务,请求出至少拿走多少个光学元件后,存在一条光线线路可以从CD射出。

下面举例说明:

现在假设,取走中间那个矩形,那么就可以构造出一条穿过通道的光路,如图中的S到T。

Input

第一行包含两个整数,x,y,表示C点坐标

第二行包含一个数字,n,表示有n个光学元件

接下来n行

第一个数字如果是1,表示元件α,后面会有三个整数xi,yi,ri分别表示圆心坐标和半径

第一个数字如果是2,表示元件β,后面会有四个整数x1,y1,x2,y2分别表示左下角和右上角坐标(矩形都平行,垂直于坐标轴)

Output

 

输出包含一行,至少需要拿走的光学元件个数m

Sample Input

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

Sample Output

2

HINT

x<=100000,y<=1000,n<=300


 

Source


这个题啊,Excited!网络流最小割嘛,把每个图形拆点,即第i个图形向第i+n个图形连流量为1的无向边

如果一个图形与另一个图形相交,就连流量为INF的无向边

判相交的话,就是矩形与圆相交,搞一个计算几何求点到线段距离就好了

PS:与边界相交可以把边界视为矩形搞。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 910
#define maxm 300100
#define pf(x) (1ll*(x)*(x))
using namespace std;
int n,bg,ed,head[maxn],dep[maxn],q[maxn],ql,qr,esz=2;
long long x,y;
struct edge{
    int r,nxt,w;
}e[maxm];
struct Vector{
    double x,y;
    Vector(){}
    Vector(double x,double y):x(x),y(y){}
    Vector operator-(const Vector& vec)const{
        return Vector(x-vec.x,y-vec.y);
    }
};
double cross(const Vector& a,const Vector& b){
    return a.x*b.y-a.y*b.x;
}
double dot(const Vector& a,const Vector& b){
    return a.x*b.x+a.y*b.y;
}
double length(const Vector& a){
    return sqrt((double)dot(a,a));
}
double distoseg(const Vector x,const Vector a,const Vector b){
    Vector v1=b-a,v2=x-a,v3=x-b;
    if(dot(v1,v2)<0)return length(v2);
    if(dot(v1,v3)>0)return length(v3);
    return ((double)abs(cross(v1,v2)))/length(v1);
}
int dcmp(double a){
    return fabs(a)<1e-7?0:(a>0?1:-1);
}
struct data{
    long long x1,y1,x2,y2,att;
    void circle(long long x,long long y,long long r){
        x1=x;y1=y;x2=r;att=1;
    }
    void square(long long x1,long long y1,long long x2,long long y2){
        this->x1=x1;this->x2=x2;
        this->y1=y1;this->y2=y2;
        att=2;
    }
}d[maxn];
void addedge(int u,int v,int w){
//    printf("[%d,%d]\n",u,v);
    e[esz].r=v;e[esz].nxt=head[u];
    e[esz].w=w;head[u]=esz++;
    e[esz].r=u;e[esz].nxt=head[v];
    e[esz].w=0;head[v]=esz++;
}
bool isincir(long long x1,long long y1,data& d){
    return pf(x1-d.x1)+pf(y1-d.y1)<=pf(d.x2);
}
bool isinsq(long long x1,long long y1,data& d){
    return d.x1<=x1&&x1<=d.x2&&d.y1<=y1&&y1<=d.y2;
}
bool cirandcir(data& x,data& y){
    return pf(x.x1-y.x1)+pf(x.y1-y.y1)<=pf(x.x2+y.x2);
}
bool pd(long long x1,long long y1,long long x2,long long y2,long long x,long long y,long long r){
    return dcmp(distoseg(Vector(x,y),Vector(x1,y1),Vector(x2,y2))-r)<=0;
}
bool bfs(){
    ql=qr=0;
    q[qr++]=bg;
    memset(dep,0,sizeof(dep));
    dep[bg]=1;
    while(ql<qr){
        int x=q[ql++];
        for(int t=head[x];t;t=e[t].nxt)if(!dep[e[t].r]&&e[t].w)
            dep[e[t].r]=dep[x]+1,q[qr++]=e[t].r;
    }
    return dep[ed]!=0;
}
int find(int u,int flow){
    if(u==ed)return flow;
    int a=0,used=0;
    for(int t=head[u];t;t=e[t].nxt)if(e[t].w&&dep[e[t].r]==dep[u]+1&&(a=find(e[t].r,min(flow,e[t].w)))){
        e[t].w-=a;e[t^1].w+=a;
        used+=a;
        if(used==flow)return used;
         
    }
    if(!used)dep[u]=0;
    return used;
}
bool check(int i,int j){
    if(d[i].att==1&&d[j].att==1){
        if(cirandcir(d[i],d[j])||cirandcir(d[i],d[j]))
            return true;
    } else if(d[i].att==2&&d[j].att==2){
        if(isinsq(d[i].x1,d[i].y1,d[j])||isinsq(d[i].x2,d[i].y1,d[j])
            ||isinsq(d[i].x1,d[i].y2,d[j])||isinsq(d[i].x2,d[i].y2,d[j])||
            isinsq(d[j].x1,d[j].y1,d[i])||isinsq(d[j].x2,d[j].y1,d[i])
            ||isinsq(d[j].x1,d[j].y2,d[i])||isinsq(d[j].x2,d[j].y2,d[i]))
                        return true;
    } else {
        int ni=i,nj=j;
        if(d[ni].att==2&&d[nj].att==1)swap(ni,nj);
        if(pd(d[nj].x1,d[nj].y1,d[nj].x1,d[nj].y2,d[ni].x1,d[ni].y1,d[ni].x2))return true;
        else if(pd(d[nj].x1,d[nj].y2,d[nj].x2,d[nj].y2,d[ni].x1,d[ni].y1,d[ni].x2))return true;
        else if(pd(d[nj].x2,d[nj].y2,d[nj].x2,d[nj].y1,d[ni].x1,d[ni].y1,d[ni].x2))return true;
        else if(pd(d[nj].x2,d[nj].y1,d[nj].x1,d[nj].y1,d[ni].x1,d[ni].y1,d[ni].x2))return true;
    }
    return false;
}
int main(){
    scanf("%lld%lld%d",&x,&y,&n);
    for(int i=1;i<=n;++i){
        int op;scanf("%d",&op);
        if(op==1){
            long long x,y,r;scanf("%lld%lld%lld",&x,&y,&r);
            d[i].circle(x,y,r);
        } else {
            long long  x1,y1,x2,y2;
            scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
            d[i].square(x1,y1,x2,y2);
        }
    }
    d[n+1].square(0,y,x,1ll<<60);
    d[n+2].square(0,-1ll<<60,1ll<<60,0);
    for(int i=1;i<=n;++i)addedge(i,i+n,1),addedge(i+n,i,1);
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
            if(check(i,j)) addedge(i+n,j,1<<30),addedge(j+n,i,1<<30);
	bg=0,ed=n*2+1;
    for(int i=1;i<=n;++i)if(check(n+1,i))addedge(i+n,ed,1<<30);
    for(int i=1;i<=n;++i)if(check(n+2,i))addedge(bg,i,1<<30);
    int ans=0,f=0;
    while(bfs())while(f=find(bg,1<<30))ans+=f;
    printf("%d",ans);
    
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值