「BZOJ2960」跨平面 题解

对偶图+朱刘算法

Statement

img

输入第一行两个整数n和m,表示点与线段的数目。

接下来n行,每行两个整数x和y,表示第i个点的坐标,点从1到n编号。

接下来m行,每行四个整数p,q,V1和V2,表示存在一条从第p个点连向第q个点的线段,激活p->q这个方向的费用为V1,另一个方向费用为V2。

保证若两条线段相交,则交点是它们的公共端点。

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

Solution

平面图转对偶图后便是裸的最小生成图了

转对偶图的难点在于找到一个多边形

我们考虑拆成两条边,意图让每一条有向边染色,使得同色的边顺次相连会包围出一个多边形

所以我们考虑每次找到一条尚未染色的边开始 dfs

先对每一条边按照其角度(斜率)排序

对于上一个点 \(fath\) ,当前走点 \(u\) ,我们意图找到一条边 \((u,v)\) ,使得上一条边是 \((u,fath)\)

因为事前排过序,所以这样会围出一个最小的多边形

同时由于我们拆出了两条边,所以不会出现一条边被染色两次的情况

转完对偶图,由于不定根,加一个超级根即可

注意题目保证有解

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;

char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
    return s*w;
}
bool chkmin(int &a,int b){return a>b?a=b,1:0;}

namespace MST{
    struct Edge{int u,v,w;}edge[N];
    int pre[N],prv[N],vis[N],id[N];
    int n,cnt,rt,elen,ans,sum;

    void addedge(int u,int v,int w){edge[++elen]=(Edge){u,v,w};}
    int mst(){
        for(int i=1;i<=elen;++i)sum+=edge[i].w;
        for(int i=1;i<=n;++i)addedge(0,i,sum+1);
        rt=0,ans-=sum+1;
        while(1){
            for(int i=0;i<=n;++i)pre[i]=1e18;
            for(int i=1,u,v;u=edge[i].u,v=edge[i].v,i<=elen;++i)
                if(u!=v&&chkmin(pre[v],edge[i].w))prv[v]=u;
            memset(vis,-1,sizeof(vis)),memset(id,-1,sizeof(id)),cnt=pre[rt]=0;
            for(int i=0,v;i<=n;++i){
                ans+=pre[i],v=i;
                while(v!=rt&&id[v]==-1&&vis[v]!=i)vis[v]=i,v=prv[v];
                if(v!=rt&&id[v]==-1){
                    for(int u=prv[v];u^v;u=prv[u])id[u]=cnt;
                    id[v]=cnt++;
                }
            }
            if(!cnt)break;
            for(int i=0;i<=n;++i)if(id[i]==-1)id[i]=cnt++;
            for(int i=1,u,v;u=edge[i].u,v=edge[i].v,i<=elen;++i)
                edge[i].u=id[u],edge[i].v=id[v],edge[i].w-=(id[u]!=id[v])*pre[v];
            n=cnt-1,rt=id[rt];
        }
        return ans; 
    }
}
struct Edge{int u,v,nex,id,tp;}edge[N];
struct Line{
    int u,v,id,tp;double alpha;
    bool operator<(const Line&rhs)const{
        return alpha<rhs.alpha;
    }
}line[N];
int x[N],y[N],head[N],val[2][N],id[2][N];
int n,m,elen,num;
bool vis[N];

void addedge(int u,int v,int id,int tp){
    edge[++elen]=(Edge){u,v,head[u],id,tp},head[u]=elen;
}
int dfs(int u,int fath){
    if(vis[u])return ++MST::n;
    for(int e=head[u],v;v=edge[e].v,e!=-1;e=edge[e].nex)if(v==fath)
        return (e=edge[e].nex==-1?head[u]:edge[e].nex),id[edge[e].tp][edge[e].id]=dfs(edge[e].v,u);
    return 0;
}

signed main(){
    n=read(),m=read(),memset(head,-1,sizeof(head));
    for(int i=1;i<=n;++i)x[i]=read(),y[i]=read();
    for(int i=1,u,v;i<=m;++i)
        u=read(),v=read(),val[0][i]=read(),val[1][i]=read(),
        line[++num]=(Line){u,v,i,0,atan2(y[v]-y[u],x[v]-x[u])},
        line[++num]=(Line){v,u,i,1,atan2(y[u]-y[v],x[u]-x[v])};
    sort(line+1,line+1+num);
    for(int i=1;i<=num;++i)addedge(line[i].u,line[i].v,line[i].id,line[i].tp);
    for(int i=1;i<=elen;++i)
        if(!id[edge[i].tp][edge[i].id])
            vis[edge[i].u]=1,
            id[edge[i].tp][edge[i].id]=dfs(edge[i].v,edge[i].u),
            vis[edge[i].u]=0;
    for(int i=1;i<=m;++i){
        if(val[0][i])MST::addedge(id[0][i],id[1][i],val[0][i]);
        if(val[1][i])MST::addedge(id[1][i],id[0][i],val[1][i]);
    }
    printf("%lld\n",MST::mst());
    return 0;
}
posted @ 2022-01-23 20:03  _Famiglistimo  阅读(39)  评论(0编辑  收藏  举报