https://vjudge.net/problem/Gym-101482F
思路:给出 n n n个点的坐标,问是否存在一条直线,使得至少有 % p \%p %p的点位于该条直线上。
思路: n 2 n^2 n2枚举?肯定会 T T T。我们假设最有 m m m个点位于一条直线上,每次任意选择两个点,选择到它们中的 2 2 2个点的概率是 C ( m , 2 ) / C ( n , 2 ) C(m,2)/C(n,2) C(m,2)/C(n,2),化简可得 m ∗ ( m − 1 ) / ( n ∗ ( n − 1 ) ) m*(m-1)/(n*(n-1)) m∗(m−1)/(n∗(n−1)),由于 p p p最少取 20 20 20,所以上述概率最低约为 1 / 25 1/25 1/25,这给了我们启发:每次随机选取两个点,选取 k k k次,那么得到的结果是正确的概率就高达 k / 25 k/25 k/25,显然 k k k可以取的很大,所以结果很有可能是正确的。计算就不用多说了,注意用乘法不要用除法。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pr pair<int,int>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,p;
ll x[maxn],y[maxn];
ll bx1,by1,bx2,by2;
bool check(ll dx,ll dy)
{
ll r1=(by2-by1)*(dx-bx2);
ll r2=(bx2-bx1)*(dy-by2);
return r1==r2;
}
int main()
{
srand(time(nullptr));
scanf("%d%d",&n,&p);
for(int i=1;i<=n;i++)
scanf("%lld %lld",&x[i],&y[i]);
if(n<=2)
printf("possible\n");
else
{
int t=1000,id1,id2,ct;
bool flag=0;
int res=ceil(n*p*1.0/100);
while(t--&&!flag)
{
id1=(ll)rand()*rand()%n+1;
id2=(ll)rand()*rand()%n+1;
while(id2==id1)
id2=rand()%n+1;
bx1=x[id1],by1=y[id1];
bx2=x[id2],by2=y[id2];
ct=2;
for(int i=1;i<=n;i++)
if(i!=id1&&i!=id2)
ct+=check(x[i],y[i]);
if(ct>=res)
flag=1;
}
printf("%s\n",flag?"possible":"impossible");
}
return 0;
}