您的位置:首页 > 其它

BZOJ3630: [JLOI2014]镜面通道 最小割

2016-09-24 23:14 246 查看

BZOJ 3630: [JLOI2014]镜面通道

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 467  Solved: 162
[Submit][Status][Discuss]

题解:
物理中有一条定理,大概意思就是如果这个镜面通道水能够过去,光就能过去
把上面的横线作为源点,把下面的线作为汇点,如果矩形或者圆形或者这两条线之间有交,就把这两个点连一条边其流量为inf,再拆点把中间流量设成1,求最小割就好了(本题最重要的地方就是判断各种相交)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define LL long long
const LL N=1005;
const LL M=500005;
const LL inf=2e9;
struct rectangle{LL xl,yl,xr,yr;} r
;
struct circle{LL x,y,r;} c
;
LL to[M],nxt[M],w[M],lj
,cnt=1;
void insert(LL f,LL t,LL p)
{
cnt++,to[cnt]=t,nxt[cnt]=lj[f],lj[f]=cnt,w[cnt]=p;
cnt++,to[cnt]=f,nxt[cnt]=lj[t],lj[t]=cnt,w[cnt]=0;
}
queue<int>Q;
LL d
,S,T=1000;
bool bfs()
{
memset(d,0,sizeof(d));
d[S]=1;
Q.push(S);
while(!Q.empty())
{
LL x=Q.front();
Q.pop();
for(LL i=lj[x];i;i=nxt[i])
if(w[i]&&!d[to[i]])
{
d[to[i]]=d[x]+1;
Q.push(to[i]);
}
}
return d[T]>0;
}
LL dfs(LL x,LL v)
{
if(x==T||v==0) return v;
LL ret=0;
for(LL i=lj[x];i;i=nxt[i])
if(d[to[i]]==d[x]+1)
{
LL f=dfs(to[i],min(v,w[i]));
w[i]-=f;
w[i^1]+=f;
v-=f;
ret+=f;
if(v==0) break;
}
return ret;
}
LL sqr(LL x)
{return x*x;}
bool checkc(LL i,LL j)
{return sqr(c[i].r+c[j].r)>=sqr(c[i].x-c[j].x)+sqr(c[i].y-c[j].y);}
bool checkr(LL i,LL j)
{return r[i].xl<=r[j].xr&&r[j].xl<=r[i].xr&&r[i].yl<=r[j].yr&&r[j].yl<=r[i].yr;}
bool checkcr(LL i,LL j)
{
if(sqr(c[i].x-r[j].xl)+sqr(c[i].y-r[j].yl)<=sqr(c[i].r)) return true;
if(sqr(c[i].x-r[j].xl)+sqr(c[i].y-r[j].yr)<=sqr(c[i].r)) return true;
if(sqr(c[i].x-r[j].xr)+sqr(c[i].y-r[j].yl)<=sqr(c[i].r)) return true;
if(sqr(c[i].x-r[j].xr)+sqr(c[i].y-r[j].yr)<=sqr(c[i].r)) return true;
if(c[i].x<=r[j].xr&&c[i].x>=r[j].xl)
if(abs(c[i].y-r[j].yl)<=c[i].r||abs(c[i].y-r[j].yr)<=c[i].r) return true;
if(c[i].y<=r[j].yr&&c[i].y>=r[j].yl)
if(abs(c[i].x-r[j].xr)<=c[i].r||abs(c[i].x-r[j].xl)<=c[i].r) return true;
if(c[i].x+c[i].r<=r[j].xr&&c[i].x-c[i].r>=r[j].xl&&c[i].y+c[i].r<=r[j].yr&&c[i].y-c[i].r>=r[j].yl) return true;
return false;
}
LL xc,yc,n,Lc,Lr;
void build()
{
for(LL i=1;i<=Lc;i++)
{
if(c[i].y+c[i].r>=yc) insert(S,i*2-1,inf);
if(c[i].y-c[i].r<=0) insert(i*2,T,inf);
}
for(LL i=Lc+1;i<=Lc+Lr;i++)
{
if(r[i-Lc].yr>=yc) insert(S,i*2-1,inf);
if(r[i-Lc].yl<=0) insert(i*2,T,inf);
}
for(LL i=1;i<Lc;i++)
for(LL j=i+1;j<=Lc;j++)
if(checkc(i,j))
{
insert(i*2,j*2-1,inf);
insert(j*2,i*2-1,inf);
}
for(LL i=Lc+1;i<Lr+Lc;i++)
for(LL j=i+1;j<=Lr+Lc;j++)
if(checkr(i-Lc,j-Lc))
{
insert(i*2,j*2-1,inf);
insert(j*2,i*2-1,inf);
}
for(LL i=1;i<=Lc;i++)
for(LL j=Lc+1;j<=Lr+Lc;j++)
if(checkcr(i,j-Lc))
{
insert(i*2,j*2-1,inf);
insert(j*2,i*2-1,inf);
}
}
int main()
{
scanf("%lld%lld",&xc,&yc);
scanf("%lld",&n);
for(LL i=1;i<=n;i++)
{
LL o;
scanf("%lld",&o);
if(o==1)
{Lc++;scanf("%lld%lld%lld",&c[Lc].x,&c[Lc].y,&c[Lc].r);}
if(o==2)
{Lr++;scanf("%lld%lld%lld%lld",&r[Lr].xl,&r[Lr].yl,&r[Lr].xr,&r[Lr].yr);}
insert(i*2-1,i*2,1);
insert(i*2,i*2-1,1);
}
build();
LL ans=0;
while(bfs()) ans+=dfs(S,inf);
printf("%lld",ans);
}


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



[Submit][Status][Discuss]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  BZOJ 最小割