hdu 5738 Eureka(组合数)

Eureka

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1747    Accepted Submission(s): 499



Problem Description
Professor Zhang draws n points on the plane, which are conveniently labeled by 1,2,...,n . The i -th point is at (xi,yi) . Professor Zhang wants to know the number of best sets. As the value could be very large, print it modulo 109+7 .

A set P ( P contains the label of the points) is called best set if and only if there are at least one best pair in P . Two numbers u and v (u,vP,uv) are called best pair, if for every wP , f(u,v)g(u,v,w) , where f(u,v)=(xuxv)2+(yuyv)2 and g(u,v,w)=f(u,v)+f(v,w)+f(w,u)2 .
 

Input
There are multiple test cases. The first line of input contains an integer T , indicating the number of test cases. For each test case:

The first line contains an integer n (1n1000) -- then number of points.

Each of the following n lines contains two integers xi and yi (109xi,yi109) -- coordinates of the i -th point.
 

Output
For each test case, output an integer denoting the answer.
 

Sample Input
  
  
3 3 1 1 1 1 1 1 3 0 0 0 1 1 0 1 0 0
 

Sample Output
  
  
4 3 0
 

Author
zimpha
 

Source
 
题意:给你若干个点,任选两个不同的点(可以重合,但必须是两个点),求所有与这两个点共线的点数(本身也算),求有多少种选取方式。。

做法:很容易可以想到存储斜率,但不能用double,那就用向量的方式存储就好了。但是这里有几个要处理的地方;

1. 重合的点要如何处理。

2. 用向量存储的时候平行的向量会存到一起,但是题意是不被允许的。

多校赛的时候是想将向量存储的时候设置一个起点,用以区分平行向量。
但这个起点是有很多讲究的,故而并未处理成功。


后来参考了一个思路,确实是厉害.

1. 先将所有点排个序,这样可以保证向量都是非负的,去除相反向量带来的影响。
2. 用gcd的方式将向量的值最小化,使相同向量的唯一性。

当然这两步都比较正常,一般可以做到。

3. 取出一个点进行单独讨论,统计这一个点对总答案的贡献度。

这一步将向量平行的可能去除,把这个问题瞬间简单到非人的地步。。

当然这也是比较常用的方式,只不过大脑不太够用。

代码;
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
using namespace std;
struct node
{
    int x,y;
    bool operator<(const node&p)const
    {
        if(x!=p.x)
            return x<p.x;
        return y<p.y;
    }
};
const int maxn=1001;
node A[maxn];
map<node,int>p;
map<node,int>::iterator it;
typedef long long ll;
const int mod=1e9+7;
ll num[maxn];
int main()
{
    int T;
    num[0]=1;
    for(int i=1;i<maxn;i++)
    {
        num[i]=num[i-1]*2;
        num[i]%=mod;
    }
    scanf("%d",&T);
    while(T--)
    {
        int n;
        ll ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d%d",&A[i].x,&A[i].y);
        for(int i=0;i<n;i++)
        {
            int len=1;
            p.clear();
            for(int j=i+1;j<n;j++)
            {
                if(A[i].x==A[j].x&&A[i].y==A[j].y)
                {
                    len++;
                    continue;
                }
                int xx=A[i].x-A[j].x,yy=A[i].y-A[j].y;
                int k=__gcd(xx,yy);
                if(k!=0)
                {
                    xx/=k;
                    yy/=k;
                }
                p[node{xx,yy}]++;
            }
            if(len>1)
            {
                ans+=num[len-1]-1;
                ans%=mod;
            }
            for(it=p.begin();it!=p.end();it++)
            {
                int mm=it->second;
                ans+=((ll)num[len-1])*((ll)num[mm]-1);
                ans%=mod;
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值