cf140f
*2600
好题。
前置:
- \((x_1,y_1)\) 与 \((x_2,y_2)\) 的连线中点是 \((\frac{x_1+x_2}{2},\frac{y_1+y_2}{2})\)
- 若两点 \((x_1,y_1)\) 和 \((x_2,y_2)\) 关于 \((x_3,y_3)\) 中心对称,则有 \(2x_3=x_1+x_2,2y_3=y_1+y_2\) (1 的逆用)。
先考虑一个性质:
先把所有点按照 \(x\) 为第一关键字,\(y\) 为第二关键字排序。那么我们取前 \(k+1\) 和后 \(k+1\) 个点,两两取连线的中点,那么所有可能的答案一定在这些中点之中。
为什么呢?这意味着两边 \(k+1\) 个点必须会删掉一边,那么操作数量就爆了。所以现在就是要 \(check\) 一个点是否可以通过删除 \(k\) 个以内的点变成剩下的点的中心点。
我们知道一些点如果有中心点,那么一定可以两两配对使得配对的两个点连线的中点都是中心点。如果我们直接考虑这个只能做到指数级别(mask dp),但是如果我们把所有点按照 \(x\) 为第一关键字,\(y\) 为第二关键字排序后,可以发现一定是首尾配对。因为如果有交叉的配对方案,那么交叉的这两个配对他们的连线中点一定不一样(因为相较于其中一个配对,另一个配对一定 \(x, y\) 至少会有所改变)。
所以我们就可以 Two Pointers 来求出最少的删点数了。如果当前两个指针可以配对,那么直接配显然是不劣的。否则显然此时必须删掉两个指针种的一个。这个就取决于当前配对的连线中点与 \(check\) 的点的关系了,如果连线中点在 \(check\) 点的上方或者在 \(check\) 点所在行的左侧就需要删除左指针,否则删除右指针。(原因见排序)。
这样就可以实现 \(check\) 一个点了,最后把所有满足的都输出即可。
最后还需要特判无限个的情况,这个显然就是 \(k \geq n\) 了,我全删完就爆了。
最后放下代码供参考。
Code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<utility>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<bitset>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define debug(x) cout<<#x<<'='<<x<<'\n'
#define pb push_back
#define ll long long
#define x0 __x00
#define y0 __y00
#define ull unsigned long long
#define db double
#define ldb long double
#define pii pair<int,int>
#define pll pair<long long,long long>
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
template<typename tp> inline void tomax(tp &a,tp b){a=(b>a?b:a);}
template<typename tp> inline void tomin(tp &a,tp b){a=(b<a?b:a);}
template<typename tp> inline void read(tp& n){
tp x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
n=x*f;
}
template<typename tp> inline void print(tp x){
if (x<0)putchar('-'),x=-x;
if (x>9)print(x/10);
putchar(x%10+'0');
}
const int N=2e5+5;
int n,k;
struct Node{
int x,y;
bool operator<(const Node&W)const{
if(x^W.x)return x<W.x;
else return y<W.y;
}
}a[N];
bool check(int x,int y){
int i=1,j=n,s=0;
while(i<=j){
if(a[i].x+a[j].x==x&&a[i].y+a[j].y==y)i+=1,j-=1;
else if(a[i].x+a[j].x<x||(a[i].x+a[j].x==x&&a[i].y+a[j].y<y))i+=1,s+=1;
else j-=1,s+=1;
}
return s<=k;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>k;
if(k>=n){
cout<<-1<<'\n';
return 0;
}
k=min(k,n);
rep(i,1,n)cin>>a[i].x>>a[i].y;
sort(a+1,a+1+n);
set<pair<double,double> >ans;
rep(i,1,min(k+1,n))rep(j,max(1,n-k),n)if(check(a[i].x+a[j].x,a[i].y+a[j].y)){
ans.insert({1.0*(a[i].x+a[j].x)/2,1.0*(a[i].y+a[j].y)/2});
}
cout<<ans.size()<<'\n';
for(auto x:ans)printf("%.6lf %.6lf\n",x.fi,x.se);
return 0;
}

浙公网安备 33010602011771号