【简要题意】有n个点,求一条直线穿过至少 n 3 \frac{n}{3} 3n个点。n<=1e6,保证一定存在一条这样的直线。输出穿过任意两点的坐标。
【分析】
思前想后没有办法,结果随机化就a了。。。
考虑最坏的情况:只存在一个
n
3
\frac{n}{3}
3n的点集当中任意两点满足。则每次两点均选中的概率为
1
9
\frac{1}{9}
91。可以循环一百次,一百次后失败的概率为约为
7
1
e
6
\frac{7}{1e6}
1e67。如果你是个非酋,也只能超时一两个点。
【code】
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=1e6+1000;
struct node{int x,y;}a[maxn];
int n;
inline void read(int &x){
x=0;int fl=1;char tmp=getchar();
while(tmp<'0'||tmp>'9'){if(tmp=='-')fl=-fl;tmp=getchar();}
while(tmp>='0'&&tmp<='9') x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
x=x*fl;
}
inline int rand(int l,int r){
return 1LL*rand()*rand()%(r-l+1)+l;
}
signed main(){
srand(19260817);
cin>>n;
for(int i=1;i<=n;i++) read(a[i].x),read(a[i].y);
while(1){
int i=rand(1,n),j=rand(1,n);
while(i==j) i=rand(1,n),j=rand(1,n);
int cnt=0;
for(int k=1;k<=n;k++){
if((a[j].x-a[i].x)*(a[k].y-a[i].y)==(a[k].x-a[i].x)*(a[j].y-a[i].y))cnt++;
}
if(cnt*3>=n){
printf("%lld %lld\n",i,j);
return 0;
}
}
return 0;
}
这里有一个坑点就是,普通评测机下RAND_MAX=32768,但linux下的GCC默认为maxlongint。所以通用解法还是乘1LL再乘两次。