gym101221I Sensor Network
题意
给定 \(n\) 个点,求最大点集使得点两两之间距离 \(\leq d\)。
题解
很容易将题意转化为 \(x\) 选则 \(y\) 不能选的限制,求最多选的点数。
那么就是一般图最小点覆盖。这个图显然不是二分图, 因为三个两两距离 \(\gt d\) 的点很容易找到。
考虑枚举选出点集的直径。也就是枚举两个点,要求所有点间的距离都不超过这两个点的距离 \(r\)。
容易发现所有点都在以这两个点为圆心半径为 \(r\) 的圆交的区域内。
把这样的点选出来,考虑前面的想法,也就是题意转化为 \(x\) 选则 \(y\) 不能选。此时容易发现在图形中不存在三个两两距离\(\gt r\)的点对。为啥?因为图形按照两个圆心连线分成两块,每一块的直径都不超过\(r\),也就是不能在一块中选两个点,当然两块加起来就不能选 \(3\) 个点了。
此时图形变成了个二分图(按照在连线哪边决定是黑还是白),用dinic算最小点覆盖即可。
时间复杂度\(O(n^{4.5})\)。
代码
namespace flow{
const int N=105;
const int M=20005;
int to[M],lst[M],beg[N],w[M],e=1,cur[N],q[N],dis[M];
int n,s,t;
void init(){
for(int i=1;i<=n;++i){beg[i]=0;}
e=1;
}
void add(int u,int v,int c){
to[++e]=v;lst[e]=beg[u];beg[u]=e;w[e]=c;
to[++e]=u;lst[e]=beg[v];beg[v]=e;w[e]=0;
}
bool bfs(){
for(int i=1;i<=n;++i){cur[i]=beg[i];dis[i]=n+1;}
dis[s]=0;
int *l=q,*r=q;
*(l=r=q)=s;
while(l<=r){
for(int i=beg[*l];i;i=lst[i])if(w[i]&&chkmin(dis[to[i]],dis[*l]+1))*(++r)=to[i];
++l;
}
return dis[t]<n;
}
int dfs(int s,int t,int fl){
if(s==t)return fl;
for(int &i=cur[s];i;i=lst[i])if(w[i]&&dis[s]+1==dis[to[i]]){
int v=dfs(to[i],t,min(fl,w[i]));
if(v){w[i]-=v;w[i^1]+=v;return v;}
}
return 0;
}
int calc(){
int flow;
int ans=0;
while(bfs()){
while((flow=dfs(s,t,0x7fffffff)))ans+=flow;
}
return ans;
}
}
int n,w;
pii a[105];
int d[105][105];
vector<int> v1,v2;
int f(pii a,pii b,pii c){
return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
signed main(){
#ifdef QAQAutoMaton
freopen("I.in","r",stdin);
freopen("I.out","w",stdout);
#endif
read(n,w);
w=sqr(w);
for(int i=1;i<=n;++i)read(a[i].x,a[i].y);
for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)d[i][j]=sqr(a[i].x-a[j].x)+sqr(a[i].y-a[j].y);
flow::n=n+2;
flow::s=n+1;flow::t=n+2;
int ans=1;
int wx,wy;
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j)if(d[i][j]<=w){
int l=d[i][j];
int c=2;
v1.clear();v2.clear();
flow::init();
for(int k=1;k<=n;++k)if(k!=i && k!=j && d[i][k]<=l&&d[j][k]<=l){
++c;
int lr=f(a[i],a[j],a[k]);
if(lr>0)v1.emplace_back(k);
else if(lr<0)v2.emplace_back(k);
}
for(auto x:v1)flow::add(n+1,x,1);
for(auto y:v2)flow::add(y,n+2,1);
for(int x:v1)for(int y:v2)if(d[x][y]>l)flow::add(x,y,inf);
if(chkmax(ans,c-flow::calc()))wx=i,wy=j;
}
}
write(ans,'\n');
if(ans==1)return write("1\n");
int i=wx,j=wy;
int l=d[i][j];
int c=2;
v1.clear();v2.clear();
flow::init();
write(i,' ',j);
for(int k=1;k<=n;++k)if(k!=i && k!=j && d[i][k]<=l&&d[j][k]<=l){
++c;
int lr=f(a[i],a[j],a[k]);
if(lr>0)v1.emplace_back(k);
else if(lr<0)v2.emplace_back(k);
else write(' ',k);
}
for(auto x:v1)flow::add(n+1,x,1);
for(auto y:v2)flow::add(y,n+2,1);
for(int x:v1)for(int y:v2)if(d[x][y]>l)flow::add(x,y,1);
flow::calc();
using flow::dis;
for(auto x:v1)if(dis[x]!=n+3)write(' ',x);
for(auto y:v2)if(dis[y]==n+3)write(' ',y);
putc('\n');
return 0;
}