hdu 5738 Eureka 极角排序 计数

比赛的时候用枚举直线没做出来,QAQ。按照官方题解敲了一遍。

xjb推导一下可以知道best set一定是一些共线的点, 于是问题变成问有多少个子集共线. 首先, 把所有点按照(x,y)(x,y)双关键字排序, 然后枚举最左边的点ii, 那么其他点jj一定满足j > ij>i. 把在这个点右边的点都做下极角排序(按照\frac{1}{gcd(dx, dy)}(dx, dy)gcd(dx,dy)1(dx,dy)排序), 统计下共线的就好了. 需要注意下对重点的处理.

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<cmath>
#include<string>
#include<algorithm>
#include<set>
#include<map>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
//#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;

typedef long long ll;
const int maxn=1000+12;
const ll inf=1e9;
const ll mod=1e9+7;

ll pw[maxn];

struct point {
    ll x,y;
    ll s,t;
    void gcd(){
        if(s==0||t==0){
            if(s==0)t=1;
            else if(t==0)s=1;
        }
        else{
            int tt=__gcd(s,t);
            s/=tt;t/=tt;
        }
    }
    bool operator<(point zz)const {
        if(s==zz.s)return t<zz.t;
        return s<zz.s;
    }
}p[maxn],tp[maxn];


bool cmp1(const point& p1,const point &p2){
    if(p1.y==p2.y)return p1.x<p2.x;
    return p1.y<p2.y;
}


int main(){
    pw[0]=1;
    for(int i=1;i<maxn;i++){
        pw[i]=(pw[i-1]*2)%mod;
    }
	int t;
	scanf("%d",&t);
	while(t--){
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%I64d",&p[i].x);
            scanf("%I64d",&p[i].y);
        }
        sort(p,p+n,cmp1);

        ll ans=0;
        for(int i=0;i<n-1;i++){
            int siz=0,cntp=0;
            point now=p[i];
            for(int j=i+1;j<n;j++){
                if(p[j].x==now.x&&p[j].y==now.y){cntp++;continue;}
                tp[siz]=p[j];
                tp[siz].s=p[j].x-p[i].x;
                tp[siz].t=p[j].y-p[i].y;
                tp[siz++].gcd();
            }
            sort(tp,tp+siz);
            ll cntl=0;
            for(int j=0;j<siz;){//i,j make sure a line
                int k,num=cntp+1;
                for(k=j+1;k<siz;k++){
                    if((tp[k].x-now.x)*(tp[j].y-now.y)!=(tp[k].y-now.y)*(tp[j].x-now.x) )
                        break;
                    num++;
                }
                j=k;
                cntl++;
                ans=(ans+pw[num]-1+mod)%mod;
            }
            ans=(ans-(cntl-1)*(pw[cntp]-1+mod)%mod+mod)%mod;
        }
        printf("%I64d\n",ans);
	}
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值