隐藏
「bzoj2960」跨平面 - 对偶图+最小树形图 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「bzoj2960」跨平面 - 对偶图+最小树形图

题目大意


题目分析

先将原图转化为对偶图,然后建虚点跑朱刘算法即可。

如何转对偶图?
对每个点的出边极角排序。

将每个点入边指向逆时针的第一个出边。

对每条边进行Dfs,沿着指针访问,将途中的所有边标记为一个新连通块的边界。
这样我们就可以得到每个点。
跨越一条入边与对应的出边就是对偶图上的边。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

const int maxn=5005;

struct Point {
int x,y;
} p[maxn];

struct Edge {
int from,to,dist;
int id,bj;
double alpha;
Edge() {from=to=dist=id=bj=alpha=0;}
Edge(int x,int y,int v):from(x),to(y),dist(v) {}
Edge(int x,int y,int v,int num):from(x),to(y),dist(v),id(num),bj(0) {
alpha=atan2(p[y].y-p[x].y,p[y].x-p[x].x);
}
bool operator < (const Edge& b) const {
return alpha<b.alpha;
}
};

vector<Edge>edges,a,Out[maxn];
int n,m,cnt=0,vst[maxn],To[maxn];

void Dfs(int Now,int id) {
if(vst[Now])return;
vst[Now]=1;
edges[Now].bj=id;
Dfs(To[Now],id);
}

void build() {
for(int i=1; i<=n; i++) {
sort(Out[i].begin(),Out[i].end());
int in=Out[i][0].id^1,out=Out[i].back().id;
To[in]=out;
for(int j=1; j<Out[i].size(); j++) {
in=Out[i][j].id^1,out=Out[i][j-1].id;
To[in]=out;
}
}
n=m=0;
for(int i=0; i<cnt; i++)
if(!vst[i])Dfs(i,++n);
for(int i=0; i<cnt; i++)
if(edges[i].dist)a.push_back(Edge(edges[i].bj,edges[i^1].bj,edges[i].dist));
for(int i=1; i<=n; i++)a.push_back(Edge(0,i,10000));
}

int Directed_MST(int n,vector<Edge>edges,int root) {
int ans=0;
static int in[maxn];
static int id[maxn],pre[maxn],vst[maxn];
while(true) {
//找最小入边
for(int i=1; i<=n; i++)in[i]=INT_MAX/2;
for(int i=0; i<edges.size(); i++) {
Edge& e=edges[i];
int x=e.from,y=e.to;
if(x==y)continue;
if(e.dist<in[y]) {
pre[y]=x;
in[y]=e.dist;
}
}
for(int i=1; i<=n; i++) {
if(i==root)continue;
if(in[i]==INT_MAX/2)return -1; //无解
}
//找环
memset(id,0,sizeof(id));
memset(vst,0,sizeof(vst));
int cnt=0;
in[root]=0;
for(int i=1; i<=n; i++) {
ans+=in[i];
int now;
for(now=i; vst[now]!=i&&!id[now]&&now!=root; now=pre[now])vst[now]=i; //找环
if(now!=root&&!id[now]) { //缩点
id[now]=++cnt;
for(int p=pre[now]; p!=now; p=pre[p])id[p]=cnt;
}
}
if(cnt==0)break; //无环
for(int i=1; i<=n; i++)
if(!id[i])id[i]=++cnt;
for(int i=0; i<edges.size(); i++) {
Edge& e=edges[i];
int &x=e.from,&y=e.to;
if(x!=y)e.dist-=in[y];
x=id[x];
y=id[y];
}
n=cnt;
root=id[root];
}
return ans;
}

int main() {
n=Get_Int();
m=Get_Int();
for(int i=1; i<=n; i++) {
p[i].x=Get_Int();
p[i].y=Get_Int();
}
for(int i=1; i<=m; i++) {
int x=Get_Int(),y=Get_Int(),v1=Get_Int(),v2=Get_Int();
edges.push_back(Edge(x,y,v1,cnt++));
Out[x].push_back(edges.back());
edges.push_back(Edge(y,x,v2,cnt++));
Out[y].push_back(edges.back());
}
build();
printf("%d\n",Directed_MST(n,a,0)-10000);
return 0;
}
姥爷们赏瓶冰阔落吧~