ICPC 2014 WF I

目前做的这四个题里面最神仙的一个。

考虑任意枚举一对合法点,然后画出下面这种图:

不难发现,直线上方的点之间距离必然不超过 $d$ ,直线下方两个点之间距离仍然不超过 $d$ 。

于是我们只用考虑上下点之间的关系,我们可以对上下距离大于 $d$ 的点连边,表示这俩点不可一起选。于是我们相当于求这个二分图的最大独立集,二分图的最大独立集就是最大点覆盖的补集,最大点覆盖可以在二分图匹配的网络上转成最小割做。

最小割输出方案:从 $S$ 开始在残量网络 dfs 一次,对访问到的点打标记,这些访问到的点即为 $S$ 集合,如果一个边两边一边有标记一边没有,那么我们认为这个边被割掉了。

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
143
144
145
146
147
148
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 106
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
#define P 1000000007
int n , d;
typedef double db;
struct Point {
db x , y;
Point( db x = 0 , db y = 0 ) : x(x) , y(y) {}
} A[MAXN] ;
db G[MAXN][MAXN];
inline Point operator + ( const Point& a , const Point& b ) {
return Point( a.x + b.x , a.y + b.y );
}
inline Point operator - ( const Point& a , const Point& b ) {
return Point( a.x - b.x , a.y - b.y );
}
inline db det( const Point& a , const Point& b ) {
return a.x * b.y - a.y * b.x;
}

const int maxe = 100006;
int head[maxe] , to[maxe] , nex[maxe] , wto[maxe] , ecn = -1;
void ade( int u , int v , int w ) {
to[++ ecn] = v , nex[ecn] = head[u] , wto[ecn] = w , head[u] = ecn;
to[++ ecn] = u , nex[ecn] = head[v] , wto[ecn] = 0 , head[v] = ecn;
}

// Dinic.
int s , t;
queue<int> Q;
int dep[MAXN] , cur[MAXN];
const int inf = 1e9;
bool bfs( ) {
Q.push( s );
rep( i , 1 , n + 4 ) cur[i] = head[i];
memset( dep , 0x3f , sizeof dep );
dep[s] = 0;
while( !Q.empty() ) {
int u = Q.front(); Q.pop();
for( int i = head[u] ; ~i ; i = nex[i] ) if( dep[to[i]] > inf && wto[i] )
dep[to[i]] = dep[u] + 1 , Q.push( to[i] );
}
return dep[t] < inf;
}

ll dfs( int u , ll lim ) {
if( !lim || u == t ) return lim;
ll flow = 0 , f;
for( int& i = cur[u] ; ~i ; i = nex[i] ) if( dep[to[i]] == dep[u] + 1 && ( f = dfs( to[i] , min( lim , 1ll * wto[i] ) ) ) ) {
lim -= f , flow += f , wto[i] -= f , wto[i ^ 1] += f;
if( !lim ) break;
}
return flow;
}

ll dinic( ) {
ll res = 0;
while( bfs( ) )
res += dfs( s , 0x3f3f3f3f3f3f3f3f );
return res;
}

db dist( Point a , Point b ) {
return sqrt( ( a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) );
}

vi re; int res , vis[MAXN] , ok[MAXN];
void dfs( int u ) {
vis[u] = 1;
for( int o = head[u] ; ~o ; o = nex[o] ) if( wto[o] ) {
if( !vis[to[o]] ) dfs( to[o] );
}
}

void solve() {
cin >> n >> d;
rep( i , 1 , n ) {
static int x , y;
scanf("%d%d",&x,&y);
A[i].x = x , A[i].y = y;
}
rep( i , 1 , n ) rep( j , i + 1 , n ) G[i][j] = G[j][i] = dist( A[i] , A[j] );
rep( i , 1 , n ) rep( j , i + 1 , n ) if( G[i][j] <= d ) {
// cout << i << ' ' << j << endl;
db r = G[i][j];
Point f = A[i] - A[j];
vi Gl , Gr;
int tot = 0;
rep( k , 1 , n ) if( G[i][k] <= r && G[j][k] <= r && i != k && j != k ) {
if( det( A[k] - A[j] , f ) > 0 ) Gl.pb( k ) , ++ tot;
else Gr.pb( k ) , ++ tot;
}
rep( k , 0 , n + 4 ) head[k] = -1 , vis[k] = 0;
ecn = -1;
for( int x : Gl ) for( int y : Gr ) if( G[x][y] > r )
ade( x , y , inf );
s = n + 1 , t = n + 2;
for( int x : Gl ) ade( s , x , 1 );
for( int y : Gr ) ade( y , t , 1 );
int ans = dinic( );
if( tot - ans + 2 <= res ) continue;
dfs( s );
re.clear();
re.pb( i ) , re.pb( j );
for( int o = head[s] ; ~o ; o = nex[o] ) {
int v = to[o];
if( vis[v] ) re.pb( v );
}
for( int o = head[t] ; ~o ; o = nex[o] ) {
int v = to[o];
if( !vis[v] ) re.pb( v );
}
res = re.size();
}
if( res <= 0 ) { cout << 1 << endl << 1 << endl; return; }
cout << res << endl;
for( int x : re ) printf("%d ",x);
}

signed main() {
// freopen("input","r",stdin);
// freopen("fuckout","w",stdout);
// int T;cin >> T;while( T-- ) solve();
solve();
}

文章作者: yijan
文章链接: https://yijan.co/2020/10/15/icpc-2014-wf-i/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog