HOJ 13348 Finding Lines 画直线最多点

传送门:http://acm.hnu.cn/online/?action=problem&type=show&id=13348&courseid=320

Finding Lines

Time Limit: 20000ms, Special Time Limit:50000ms, Memory Limit:65536KB
Total submit users: 25, Accepted users: 11
Problem 13348 : No special judgement
Problem description

Annabel and Richard like to invent new games and play against each other. One day Annabel has a new game for Richard. In this game there is a game master and a player. The game master draws n points on a piece of paper. The task for the player is to find a straight line, such that at least p percent of the points lie exactly on that line. Richard and Annabel have very good tools for measurement and drawing. Therefore they can check whether a point lies exactly on a line or not. If the player can find such a line then the player wins. Otherwise the game master wins the game.

There is just one problem. The game master can draw the points in a way such that it is not possible at all to draw a suitable line. They need an independent mechanism to check whether there even exists a line containing at least p percent of the points, i.e., dn · p/100e points. Now it is up to you to help them and write a program to solve this task.


Input

The input consists of:

• one line with one integer n (1 ≤ n ≤ 10^5), the number of points the game master has drawn;

• one line with one integer p (20 ≤ p ≤ 100), the percentage of points which need to lie on the line;

• n lines each with two integers x and y (0 ≤ x,y ≤ 10^9), the coordinates of a point. No two points will coincide.


Output

Output one line containing either “possible” if it is possible to find a suitable line or “impossible” otherwise.


Sample Input
5
55
0 0
10 10
10 0
0 10
3 3
5
45
0 0
10 10
10 0
0 10
3 4
Sample Output
possible
impossible
Judge Tips


Problem Source
NWERC 2014

/******************************************************************
*   给n(10w)点,画一条直线至少有ceil(n*p/100.)个点(20<=p<=100)
*   ceil取上整,做法 从n个点钟随机选取两个点枚举其余点
*   手写随机比系统随机更有效,亲测!!!
******************************************************************/
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define LL long long
int x[100001],y[100001];
int n,p;
unsigned int suiji(){
    //手写随机,42和22695477可以随机改,亲测39和246813579更快
    static unsigned int seed=42;
    return seed = 22695477*seed+1;
}
inline int online(int x1,int y1,int x2,int y2){
    if((LL)(x1)*(LL)(y2)-(LL)(x2)*(LL)(y1)==0 )return 0;
    return 1;
}
int main(){
    while(~scanf("%d%d",&n,&p)){
        for(int i=0;i<n;i++)
            scanf("%d%d",&x[i],&y[i]);
        if(n<=2){puts("possible");continue; }
        bool flag=false;
        int p1,p2,cnt;
        for(int k=0;k<200;k++){
            //while(p1=rand()%n,p2=rand()%n,p1==p2);
            //系统随机函数,k达到500还wa
            while(p1=suiji()%n,p2=suiji()%n,p1==p2);//200ac
            cnt=2;
            for(int i=0;i<n;i++){
                if(p1==i||p2==i)continue;
                if(online(x[p1]-x[i],y[p1]-y[i],x[p2]-x[i],y[p2]-y[i])==0)
                    cnt++;
                if(100*cnt>=n*p){
                    flag=true;
                    break;
                }
            }
            if(flag)break;
        }
        if(flag)puts("possible");
        else puts("impossible");
    }
    return 0;
}

/************************************************************
*   随机姿势:随机1w次选取两点map保存斜率并统计
*   斜率次数>230 去暴力n个点
*************************************************************/
#include<iostream>
#include<cmath>
#include<cstdio>
#include<map>
using namespace std;
#define LL long long
LL x[100001],y[100001];
LL n,p;
LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
struct Line{
    LL a,b,c;
    Line(LL x1=0,LL y1=0,LL x2=0, LL y2=0){
        LL d = gcd(abs(x1-x2), abs(y1-y2));
        if (x1<x2) d = -d;
        a = -(y1-y2)/d;
        b = (x1-x2)/d;
        c = a*x1 + b*y1;
    }
    bool operator < (const Line &L)const{
        if(a!=L.a)return a<L.a;
        if(b!=L.b)return b<L.b;
        return c<L.c;//不能有等号<=
    }
};
unsigned int suiji(){
    static unsigned int seed = 42;
    return seed = 22695477 * seed + 1;
}
map <Line,LL> mapt;
int main(){
    while(~scanf("%64d%I64d",&n,&p)){
        for(LL i=0;i<n;i++)scanf("%I64d%I64d",&x[i],&y[i]);
        if(n<=2){puts("possible");continue;}
        mapt.clear();
        LL p1,p2,cnt;
        for(LL k=0;k<10000;k++){
            while(p1=suiji()%n,p2=suiji()%n,p1==p2);
            mapt[Line(x[p1],y[p1],x[p2],y[p2])]++;
        }
        map<Line,LL>::iterator it;
        LL a,b,c;
        bool flag=false;
        for(it=mapt.begin();it!=mapt.end();it++){
            if(it->second > 230){
                cnt=0;
                a=it->first.a;
                b=it->first.b;
                c=it->first.c;
                for(LL i=0;i<n;i++){
                    if(a*x[i]+b*y[i]==c)
                        cnt++;
                    if(100*cnt>=p*n){
                        flag=true;
                        break;
                    }
                }
                if(flag)break;
            }
        }
        if(flag)puts("possible");else puts("impossible");
    }
    return 0;
}

#include<iostream>
#include<cmath>
#include<cstdio>
#include<set>
using namespace std;
#define LL long long
int x[100001],y[100001];
int gcd(int a,int b){return b==0?a:gcd(b,a%b); }
struct Line{
    LL a,b,c;
    Line(int x1=0,int y1=0,int x2=0,int y2=0){
        int d=gcd(abs(x1-x2),abs(y1-y2));
        if(x1<x2)d=-d;
        a = -(y1-y2)/d;
        b = (x1-x2)/d;
        c = a*x1+b*y1;
    }
    bool operator < (const Line &L)const{
        if(a!=L.a)return a<L.a;
        if(b!=L.b)return b<L.b;
        return c<L.c;
    }
};
set<Line> findline(int l,int r,int p){
    int m=(l+r)>>1;
    set<Line>S,S1;
    if(100>=p*(m-l+1)){//too little point
        for(int i=l;i<=r;i++)
            for(int j=i+1;j<=r;j++)
                S1.insert(Line(x[i],y[i],x[j],y[j]));
    }else{
        S1 = findline(l,m,p);
        set<Line> S2=findline(m+1,r,p);
        S1.insert(S2.begin(),S2.end());
    }
    set<Line>::iterator it;
    LL a,b,c;
    int cnt;
    for(it=S1.begin();it!=S1.end();it++){
        a=it->a;
        b=it->b;
        c=it->c;
        cnt=0;
        for(int i=l;i<=r;i++)
            if(a*x[i] + b*y[i] == c)
                cnt++;
        if(cnt*100 >= p*(r-l+1))
            S.insert(*it);
    }
    return S;
}
int main(){
    int n,p;
    while(~scanf("%d%d",&n,&p)){
        for(int i=0;i<n;i++)scanf("%d%d",&x[i],&y[i]);
        if(n<=2||findline(0,n-1,p).size())puts("possible");
        else puts("impossible");
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值