bzoj3630: [JLOI2014]镜面通道

bzoj3630: [JLOI2014]镜面通道


题意:

n(<=300)个圆形或矩形光学元件组成一个镜面通道,求使光能够从一端射到另一端所需要去除的光学元件最小数目。


题解

这题好妙啊……。听讲的时候一脸懵,原来还能这样的嘛……
说是有一个类似【水能流过的地方,光也能经过】的定理。好感性啊(
再感性一点地说,光学元件中间要有空隙。
再再感性一点地说,上下两个镜面不能由光学元件连通。
这不是最小割的定义嘛。

拆点,入点和出点连一条流量为1的边,互相接触的光学元件连inf,与上下镜面接触的光学元件与源/汇连inf。
建图完成,听起来好简单呀……
判相交?自己随便写写就好啦。


实现细节&&提交历程

在本校oj上交,T来T去;尝试优化,还是T来T去……结果忽然反应过来 粘的最大流板子数组开小了 所以为什么不是RE呢…
改完之后WA1个点,感觉十分不好//刚刚写的另一道网络流也莫名其妙WA一个点
骗了一下数据,发现判断圆和上下边界相交写挂了。因为手懒直接改成了判圆和(宽为0的)矩形相交,AC。


代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 303
#define M 370000
#define inf 0x7fffffff
#define ll long long
using namespace std;
int to[M],hd[M],lk[N<<1],len[M],cnt=1,T;
void add(int u,int v,int w)
{
    to[++cnt]=v,hd[cnt]=lk[u],len[cnt]=w,lk[u]=cnt;
    to[++cnt]=u,hd[cnt]=lk[v],lk[v]=cnt;
}
struct pt
{
    ll x,y;
    void read()
    {scanf("%lld%lld",&x,&y);}
}tp1,tp2;
inline ll Dis(pt a,pt b)
{return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);}
struct cir
{pt o;int r;}Cir[N];
struct rec
{pt x1,x2;}Rec[N],ss,tt;
short n,loc[N];
int X,Y,ans;
ll d,xx,yy;
bool judge(cir a,rec b)
{
    d=a.r,xx=a.o.x,yy=a.o.y;
    if(xx>=b.x1.x-d&&xx<=b.x2.x+d&&yy>=b.x1.y&&yy<=b.x2.y
    ||xx>=b.x1.x&&xx<=b.x2.x&&yy>=b.x1.y-d&&yy<=b.x2.y+d)return 1;
    tp1.x=b.x1.x,tp2.y=b.x2.y,tp2.x=b.x2.x,tp2.y=b.x1.y;d*=d;
    return Dis(a.o,tp1)<=d||Dis(a.o,tp2)<=d||Dis(a.o,b.x1)<=d||Dis(a.o,b.x2)<=d;
}
bool check(int a,int b)
{
    if(loc[a]>loc[b])swap(a,b);
    if(loc[b]<2)return((Cir[a].r+Cir[b].r)*(Cir[a].r+Cir[b].r)>=Dis(Cir[a].o,Cir[b].o));
    if(loc[a]>1)return (Rec[a].x1.x<=Rec[b].x1.x&&Rec[b].x1.x<=Rec[a].x2.x||
    Rec[a].x1.x<=Rec[b].x2.x&&Rec[b].x2.x<=Rec[a].x2.x)&&
    (Rec[a].x1.y<=Rec[b].x1.y&&Rec[b].x1.y<=Rec[a].x2.y||
    Rec[a].x1.y<=Rec[b].x2.y&&Rec[b].x2.y<=Rec[a].x2.y);
    return judge(Cir[a],Rec[b]);
}
int k,q[N<<1],h,t,dis[N<<1],cur[N<<1];
bool bfs()
{
    for(int i=0;i<=T;i++)
    dis[i]=0,cur[i]=lk[i];
    h=0,t=dis[0]=1;
    while(h<t)
    {
        k=q[h++];
        for(int i=lk[k];i;i=hd[i])
        if(len[i]&&!dis[to[i]])
        dis[q[t++]=to[i]]=dis[k]+1;
    }
    return dis[T];
}
int dfs(int x,int f)
{
    if(x==T||!f)return f;
    int r=0,cst;
    for(int &i=cur[x];i;i=hd[i])
    if(dis[to[i]]==dis[x]+1)
    {
        cst=dfs(to[i],f<len[i]?f:len[i]);
        f-=cst,len[i]-=cst;
        r+=cst,len[i^1]+=cst;
        if(!f)break;
    }
    if(!r)dis[x]=0;
    return r;
}
int main()
{
    scanf("%d%d",&X,&Y);
    ss={{0,0},{X,0}};tt={{0,Y},{X,Y}};
    scanf("%d",&n);T=n<<1|1;
    for(int i=1;i<=n;i++)
    {
        add(i,i+n,1);
        scanf("%d",loc+i);
        if(loc[i]<2)
        {
            Cir[i].o.read(),scanf("%lld",&Cir[i].r);
            if(judge(Cir[i],ss))add(0,i,inf);
            if(judge(Cir[i],tt))add(i+n,T,inf);
        }
        else
        {
            Rec[i].x1.read(),Rec[i].x2.read();
            if(Rec[i].x1.y<=0&&Rec[i].x1.x<=X&&Rec[i].x2.x>=0)add(0,i,inf);
            if(Rec[i].x2.y>=Y&&Rec[i].x1.x<=X&&Rec[i].x2.x>=0)add(i+n,T,inf);
        }
        for(int j=1;j<i;j++)
        if(check(j,i))
        add(j+n,i,inf),add(i+n,j,inf);
    }
    while(bfs())ans+=dfs(0,inf);
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值