BZOJ 2965: 保护古迹(平面图转对偶图+最小割)

124 篇文章 0 订阅
34 篇文章 1 订阅

题目
这可能是我OI生涯中最长代码的题了。如果我不打紫荆花之恋的话
UPD:紫荆花之恋可以压,这个是真的压不了
明显转对偶图之后, 2 p 2^p 2p枚举包含古迹的面,将这些面与T连流量inf的边,那么面积无限的面到T的最大流就是最小割就是保护古迹的最小花费。
那么只需要把图建出来就行了(160行)。
注意(出题人保证)没有正方形套正方形(图不联通但是套在一起???)这种情况。
Claris大佬又想多了:

首先要将这个图连通,方法是通过扫描线+set求出每个连通块最高的点上方的第一条边,然后向交点连边。

然后我怎么又被卡精度了,特殊体质。

AC Code:

#include<bits/stdc++.h>
#define eps 1e-5
#define inf 0x3f3f3f3f
#define maxn 405
#define maxm 1105
using namespace std;
 
int dcmp(double a)
{
    if(a > eps) return 1;
    if(a < -eps) return -1;
    return 0;
}
 
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
    Point operator +(const Point &B)const{ return Point(x+B.x,y+B.y); }
    Point operator -(const Point &B)const{ return Point(x-B.x,y-B.y); }
    double operator *(const Point &B)const{ return x*B.y-y*B.x; }
    bool operator <(const Point &B)const{ return dcmp(x-B.x)==0?y<B.y:x<B.x; }
}Pt[maxn];
bool cmp2(const int &u,const int &v){ return Pt[u]<Pt[v]; }
 
int p,n,m,loc[maxn];
struct Query
{
    int id;
    Point A;
    bool operator <(const Query &B)const{ return dcmp(A.x-B.A.x)==0?id<B.id:A.x<B.A.x; }
}P[15];
 
struct edge
{
    int u,v,w;
    double alp;
    edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){alp=atan2(Pt[v].y-Pt[u].y,Pt[v].x-Pt[u].x);}
    bool operator <(const edge &B)const{ return dcmp(alp-B.alp)==0?u<v:alp<B.alp; }
}e[maxm];
int id[maxm],bl[maxm],cnt_bl,infarea;
bool cmp1(const int &u,const int &v){ return e[u]<e[v]; }
 
int rk[maxm];
vector<int>G[maxn];
 
double nowx;
struct Line
{
    double k,b;
    int id;
    Line(double k=0,double b=0,int id=0):k(k),b(b),id(id){}
    bool operator <(const Line &B)const
    { 
        double a1 = k * nowx + b , a2 = B.k * nowx + B.b;
            //printf("%lf %d %d\n",nowx,id,B.id);
        return dcmp(a1-a2) == 0 ? dcmp(k - B.k) == 0 ? id < B.id : k < B.k : a1 < a2; 
    }
};
 
Line calc(Point u,Point v,int id)
{
    double k = (v.y-u.y) / (v.x-u.x) , b = u.y - k * u.x;
    //printf("%.3lf %.3lf %d\n",k,b,id);
    return Line(k,b,id);
}
 
set<Line>st;
set<Line>::iterator it,IT[maxm];
 
int info[maxn],Prev[maxm],to[maxm],cap[maxm],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c; }
void Lin(int u,int v,int c){ Node(u,v,c),Node(v,u,c); }
int binfo[maxn],bPrev[maxm],bcap[maxm],bcnt_e;
 
int bit[maxm],dis[maxm],S,T;
 
int aug(int now,int Max)
{
    if(now == T) return Max;
    int inc , st = Max;
    for(int i=info[now];i;i=Prev[i])
        if(cap[i] && dis[to[i]] + 1 == dis[now])
        {
            inc = aug(to[i] , min(cap[i] , st));
            if(inc) st -= inc , cap[i] -= inc, cap[i^1] += inc;
            else dis[to[i]] = -1;
            if(!st) break;
        }
    return Max - st;
}
 
bool BFS()
{
    static queue<int>q;
    memset(dis,-1,sizeof dis);
    q.push(T),dis[T]=0;
    for(int now;!q.empty();)
    {
        now = q.front() , q.pop();
        for(int i=info[now];i;i=Prev[i])
            if(cap[i^1] && dis[to[i]] == -1)
            {
                dis[to[i]] = dis[now] + 1;
                q.push(to[i]);
            }
    }
    return dis[S] != -1;
}
 
int sap()
{
    int stm = 0;
    for(;BFS();)
        stm += aug(S,inf);
    return stm;
}
 
int ans[maxm];
 
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    scanf("%d%d%d",&p,&n,&m);
    for(int i=1;i<=p;i++) 
        scanf("%lf%lf",&P[i].A.x,&P[i].A.y),P[i].id=i;
    for(int i=1;i<=n;i++)
        scanf("%lf%lf",&Pt[i].x,&Pt[i].y);
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        e[i<<1] = edge(a,b,c),id[i<<1]=(i<<1);
        e[i<<1|1] = edge(b,a,c),id[i<<1|1]=(i<<1|1);
    }
    sort(id+2,id+2+2*m,cmp1);
    for(int i=2;i<=2*m+1;i++)
    {
        int u = id[i];
        rk[u] = G[e[u].u].size();
        G[e[u].u].push_back(u);
    }
    for(int i=2;i<=2*m+1;i++)
        if(!bl[i])
        {
            int eid = i , x , st = e[eid].u;
            double area = 0;
            for(++cnt_bl;!bl[eid];)
            {
                area += (Pt[e[eid].u]-Pt[st]) * (Pt[e[eid].v]-Pt[st]);
                bl[eid] = cnt_bl;
                x=e[eid].v , eid^=1;
                if(rk[eid]==0) eid = G[x].back();
                else eid = G[x][rk[eid]-1];
            }
            if(area < 0) infarea = cnt_bl; 
        }
         
    //for(int i=2;i<=2*m+1;i++)
    //  printf("%d %d %d %d\n",i,e[i].u,e[i].v,bl[i]);
     
    //GPS
    for(int i=1;i<=n;i++) id[i]=i;
    sort(id+1,id+1+n,cmp2);
    sort(P+1,P+1+p);
    for(int i=1,j=1;i<=p;i++)
    {
        for(;j<=n && dcmp(P[i].A.x-Pt[id[j]].x)>=0;j++)
        {
            int u = id[j];
            for(int k=0,siz=G[u].size();k<siz;k++)
            {
                int eid = G[u][k];
                if(dcmp(Pt[e[eid].v].x - Pt[e[eid].u].x) == 0) 
                    continue;
                if(Pt[e[eid].v].x < Pt[e[eid].u].x) 
                {
                //  puts("3");
                //printf("%d %d %d\n",eid,e[eid].u,e[eid].v);
                    st.erase(IT[eid^1]);
                }
            }
             
            nowx = Pt[u].x;
            for(int k=0,siz=G[u].size();k<siz;k++)
            {
                int eid = G[u][k];
                if(dcmp(Pt[e[eid].v].x - Pt[e[eid].u].x) == 0) 
                    continue;
                if(Pt[e[eid].v].x > Pt[e[eid].u].x) 
                {
                //  puts("4");
                //printf("%d %d %d\n",eid,e[eid].u,e[eid].v);
                //  printf("%.3lf %.3lf %.3lf %.3lf\n",Pt[e[eid].u].x,Pt[e[eid].u].y,Pt[e[eid].v].x,Pt[e[eid].v].y);
                    IT[eid] = st.insert(calc(Pt[e[eid].v],Pt[e[eid].u],eid)).first;
                }
            }
        }
        if(st.empty())
        {
            loc[P[i].id] = infarea;
            continue; 
        } 
        nowx = P[i].A.x;
        it = st.lower_bound(Line(0,P[i].A.y,0));
        if(it == st.begin() || it == st.end()) 
        {
            //puts("1");
            loc[P[i].id] = infarea;
        }
        else
        {
            loc[P[i].id] = bl[(*(--it)).id];
            //puts("2");
        }
    }//puts("5");
    for(int i=1;i<=m;i++)
        if(bl[i<<1] != bl[i<<1|1])
            Lin(bl[i<<1],bl[i<<1|1],e[i<<1].w);
    memcpy(binfo,info,sizeof info);
    memcpy(bPrev,Prev,sizeof Prev);
    memcpy(bcap,cap,sizeof cap);
    bcnt_e = cnt_e; // !!
    //printf("%d\n",infarea);
    S = infarea , T = cnt_bl + 1;
    memset(ans,0x3f,sizeof ans);
    for(int i=1;i<(1<<p);i++)
    {
        memcpy(info,binfo,sizeof info);
        memcpy(Prev,bPrev,sizeof Prev);
        memcpy(cap,bcap,sizeof cap);
        cnt_e=bcnt_e;
        bit[i] = bit[i>>1] + (i&1);
        for(int j=1;j<=p;j++)
            if(i>>(j-1)&1)
                Lin(loc[j],T,inf);
        ans[bit[i]] = min(ans[bit[i]] , sap());
        //printf("%d\n",i);
    }
    for(int i=p;i>=1;i--)
        ans[i] = min(ans[i] , ans[i+1]);
    for(int i=1;i<=p;i++)
        printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值