【BZOJ2960】跨平面(对偶图+朱刘算法)

Description

这里写图片描述

Input

第一行两个整数n和m,表示点与线段的数目。
接下来n行,每行两个整数x和y,表示第i个点的坐标,点从1到n编号。
接下来m行,每行四个整数p,q,V1和V2,表示存在一条从第p个点连向第q个点的线段,激活p->q这个方向的费用为V1,另一个方向费用为V2。
保证若两条线段相交,则交点是它们的公共端点。

Output

输出一行一个正整数,表示最小总激活费用。

Sample Input

4 5

0 0

1 0

0 1

1 1

1 2 0 0

1 3 0 3

2 3 1 0

2 4 2 0

4 3 0 0

Sample Output

3

HINT

这里写图片描述

  对于100%的数据,n≤3000,区域数不超过1000,点坐标绝对值不超过1W,每条边激活费用不超过100。

题解:真是一道神题,学到了不少新姿势√
首先,不知道对偶图的请自行百度。因为这道题要用到平面图转对偶图。首先,找一条没有经过的单向边,开始深搜,直到回到自己的点为止,这样就围成了一个区域,在回溯时给经过的每条边都标上这个区域的编号。然后求它的对偶图。这就转成了一个有向图,在上面求最小树形图即可。

代码如下:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
using namespace std;
namespace Graph
{
    #define N 2005
    #define M 50005
    int X[M],Y[M],val[M],c2;  
    int nex[M],to[M],hd[N],cnt,tot;  
    int in[N],fa[N],n;  
    bool vis[N],inc[N];
    void add(int x,int y,int v)
    {
        X[++c2]=x,Y[c2]=y,val[c2]=v;
    }
    void addedge(int x,int y)
    {
        to[++cnt]=y,nex[cnt]=hd[x],hd[x]=cnt;
    }
    int dfs(int x)
    {
        inc[x]=1;
        if(inc[fa[x]]){tot+=in[x];return fa[x];}
        else 
        {
            int tmp=dfs(fa[x]);
            if(tmp && x!=tmp)
            {
                tot+=in[x];
                return tmp;
            }
            else if(x==tmp)
            {
                tot+=in[x];
                return 0;
            }
        }
        inc[x]=0;
        return 0;
    }
    int check()  
    {  
        for(int i=0;i<=n;i++)  
        if(!vis[i]) in[i]=inf,hd[i]=0,inc[i]=1;  
        for(int i=1;i<=c2;i++)  
        {  
            if(!vis[X[i]] && !vis[Y[i]])  
            if(in[Y[i]]>val[i])  
            {  
                in[Y[i]]=val[i];  
                fa[Y[i]]=X[i];  
            }   
        }  
        int ret=0;  
        for(int i=1;i<=n;i++)  
        if(!vis[i]) ret+=in[i];  
        cnt=0;  
        for(int i=1;i<=n;i++)  
        if(!vis[i]) addedge(fa[i],i);  
        queue<int> q;  
        q.push(0);  
        while(!q.empty())  
        {  
            int x=q.front();q.pop();  
            inc[x]=0;  
            for(int i=hd[x];i;i=nex[i]) q.push(to[i]);   
        }  
        for(int i=0;i<=n;i++)  
        if(!vis[i] && inc[i])  
        {  
            memset(inc,0,sizeof(inc[0])*(n+1));  
            dfs(i);  
            return -1;  
        }  
        return ret;  
    }  
    int solve()  
    {  
        int ans,sum=0;  
        for(int i=1;i<=c2;i++) sum+=val[i];  
        for(int i=1;i<=n;i++) add(0,i,sum+1);  
        tot=0;  
        while((ans=check())==-1)  
        {  
            for(int i=1;i<=c2;i++)  
            {  
                if(inc[X[i]] && !vis[Y[i]] && !inc[Y[i]]) X[i]=n+1;  
                else if(!vis[X[i]] && !inc[X[i]] && inc[Y[i]])  
                {  
                    val[i]-=in[Y[i]];  
                    Y[i]=n+1;  
                }  
            }  
            for(int i=0;i<=n;i++)  
            if(inc[i]) vis[i]=1,inc[i]=0;  
            n++;  
        }  
        return ans+tot-sum-1;  
    }  
    #undef N
    #undef M
}
namespace Plane
{
    #define N 3005
    #define M 15000
    struct Point
    {
        int x,y;
        Point(){}
        Point(int _x,int _y):x(_x),y(_y){}
    }a[N];
    struct Line
    {
        int x,y,id;
        bool v;
        double k;
        inline bool operator<(const Line& rhs) const {return k<rhs.k;}
    }l[M];
    int nex[M],to[M],num[M],hd[N],tot;
    bool v[M],vis[N];
    int val[M][2],X[M][2];
    void add(int x,int y,int nm,bool vv)
    {
        to[++tot]=y,num[tot]=nm,v[tot]=vv,nex[tot]=hd[x],hd[x]=tot;
    }
    int dfs(int x,int fa)
    {
        if(vis[x]) return ++Graph::n;
        for(int i=hd[x];i;i=nex[i])
        {
            if(to[i]==fa)
            {
                i=nex[i];
                if(i) return X[num[i]][v[i]]=dfs(to[i],x);
                else return X[num[hd[x]]][v[hd[x]]]=dfs(to[hd[x]],x); 
            }
        }
        return 0;
    }
    void solve()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&l[i*2-1].x,&l[i*2-1].y,&val[i][0],&val[i][1]);
            l[i*2-1].id=i,l[i*2-1].v=0;
            l[i*2-1].k=atan2(a[l[i*2-1].y].y-a[l[i*2-1].x].y,a[l[i*2-1].y].x-a[l[i*2-1].x].x);
            l[i*2].x=l[i*2-1].y,l[i*2].y=l[i*2-1].x,l[i*2].id=i,l[i*2].v=1;
            l[i*2].k=atan2(a[l[i*2-1].x].y-a[l[i*2-1].y].y,a[l[i*2-1].x].x-a[l[i*2-1].y].x);
        }
        sort(l+1,l+m*2+1);
        for(int i=1;i<=m*2;i++) add(l[i].x,l[i].y,l[i].id,l[i].v);  
        for(int i=1;i<=n;i++)  
        {  
            vis[i]=1;  
            for(int j=hd[i];j;j=nex[j])  
            if(!X[num[j]][v[j]]) X[num[j]][v[j]]=dfs(to[j],i);  
            vis[i]=0;  
        }  
        for(int i=1;i<=m;i++)  
        {  
            if(val[i][0]) Graph::add(X[i][0],X[i][1],val[i][0]);  
            if(val[i][1]) Graph::add(X[i][1],X[i][0],val[i][1]);  
        }  
    }
    #undef N
    #undef M
}
int main()
{
    Plane::solve();
    printf("%d\n",Graph::solve()); 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值