题目链接
(Gym) https://codeforces.com/gym/101221/
(BZOJ) 大人,时代变了。
题解
又是一道看了题解的作业题。
这是一个最大团(或者补图上的最大独立集)问题,而二分图最大独立集是可以做的,因此可以考虑转化成二分图。
枚举点集的直径的两端点 (x,y),满足 (dis(x,y)le d),那么剩下点的可选范围就是两个分别以 (x,y) 为圆心、(dis(x,y)) 为半径的圆的交。
可以发现,以两点连线为界,交的区域被分成了两部分。如果两个点距离大于 (dis(x,y)),那么它们一定位于不同的两部分。
于是天然的二分图就构建好了!在上面求最大独立集也就是用总点数减去最大匹配即可。
时间复杂度 (O(n^{4.5})),但是跑得飞快,cf 上只用 (46 ext{ms}).
另外 lk 的博客上说是 (O(n^4)),但并不知道为什么。
代码
#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
using namespace std;
inline int read()
{
int x = 0,f = 1; char ch = getchar();
for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
return x*f;
}
namespace NetFlow
{
const int N = 202;
const int M = 10200;
const int INF = 1e6;
struct Edge
{
int v,w,nxt;
} e[(M<<1)+3];
int fe[N+3];
int te[N+3];
int dep[N+3];
int que[N+3];
int n,en,s,t;
void clear()
{
for(int i=1; i<=n; i++) fe[i] = te[i] = dep[i] = que[i] = 0;
for(int i=1; i<=en; i++) e[i].v = e[i].w = e[i].nxt = 0;
n = s = t = 0; en = 1;
}
void addedge(int u,int v,int w)
{
// printf("addedge %d %d %d
",u,v,w);
en++; e[en].v = v; e[en].w = w;
e[en].nxt = fe[u]; fe[u] = en;
en++; e[en].v = u; e[en].w = 0;
e[en].nxt = fe[v]; fe[v] = en;
}
bool bfs()
{
for(int i=1; i<=n; i++) dep[i] = 0;
int head = 1,tail = 1; que[1] = s; dep[s] = 1;
while(head<=tail)
{
int u = que[head]; head++;
for(int i=fe[u]; i; i=e[i].nxt)
{
int v = e[i].v;
if(e[i].w>0 && dep[v]==0)
{
dep[v] = dep[u]+1;
if(v==t) return true;
tail++; que[tail] = v;
}
}
}
return false;
}
int dfs(int u,int cur)
{
if(u==t||cur==0) {return cur;}
int rst = cur;
for(int &i=te[u]; i; i=e[i].nxt)
{
int v = e[i].v;
if(e[i].w>0 && rst>0 && dep[v]==dep[u]+1)
{
int flow = dfs(v,min(rst,e[i].w));
if(flow>0)
{
e[i].w -= flow;
rst -= flow;
e[i^1].w += flow;
if(rst==0) {return cur;}
}
}
}
if(rst==cur) {dep[u] = -2;}
return cur-rst;
}
int dinic(int _n,int _s,int _t)
{
n = _n,s = _s,t = _t;
int ret = 0;
while(bfs())
{
for(int i=1; i<=n; i++) te[i] = fe[i];
memcpy(te,fe,sizeof(int)*(n+1));
ret += dfs(s,INF);
}
return ret;
}
void dfs2(int u)
{
dep[u] = 1;
for(int i=fe[u]; i; i=e[i].nxt) if(e[i].w>0)
{
int v = e[i].v; if(dep[v]) continue;
dfs2(v);
}
}
void calcway()
{
for(int i=1; i<=n; i++) dep[i] = 0;
dfs2(1);
}
}
using NetFlow::addedge;
using NetFlow::dinic;
const int mxN = 100;
struct Point {int x,y;};
typedef Point Vector;
Point operator +(Point x,Point y) {return (Point){x.x+y.x,x.y+y.y};}
Point operator -(Point x,Point y) {return (Point){x.x-y.x,x.y-y.y};}
int Norm2(Vector x) {return x.x*x.x+x.y*x.y;}
int Cross(Vector x,Vector y) {return x.x*y.y-x.y*y.x;}
Point a[mxN+3]; vector<int> way;
int n; int d;
int main()
{
n = read(),d = read();
for(int i=1; i<=n; i++) {a[i].x = read(),a[i].y = read();}
int ans = 1; way.push_back(1);
for(int x=1; x<=n; x++) for(int y=x+1; y<=n; y++) if(Norm2(a[x]-a[y])<=d*d)
{
vector<int> s1,s2; int cd = Norm2(a[x]-a[y]);
for(int i=1; i<=n; i++) if(i!=x&&i!=y&&Norm2(a[i]-a[x])<=cd&&Norm2(a[i]-a[y])<=cd)
{
if(Cross(a[y]-a[x],a[i]-a[x])>=0) {s1.push_back(i);} else {s2.push_back(i);}
}
NetFlow::clear();
for(int i=0; i<s1.size(); i++) addedge(1,i+3,1);
for(int i=0; i<s2.size(); i++) addedge(i+s1.size()+3,2,1);
for(int i=0; i<s1.size(); i++) for(int j=0; j<s2.size(); j++)
{
if(Norm2(a[s2[j]]-a[s1[i]])>cd) {addedge(i+3,j+s1.size()+3,1);}
}
int cur = s1.size()+s2.size()-dinic(s1.size()+s2.size()+2,1,2)+2;
if(cur>ans)
{
ans = cur; way.clear(); way.push_back(x),way.push_back(y);
NetFlow::calcway();
for(int i=0; i<s1.size(); i++) if(NetFlow::dep[i+3]) {way.push_back(s1[i]);}
for(int i=0; i<s2.size(); i++) if(!NetFlow::dep[i+s1.size()+3]) {way.push_back(s2[i]);}
}
}
printf("%d
",ans);
for(int i=0; i<way.size(); i++) printf("%d ",way[i]); puts("");
return 0;
}