2016暑假多校合练第二场Multi-University Training Contest 2 1005 Eureka


这道题前面给了两个公式,其实仔细分析一下,就会发现其实是给了你一堆点的坐标,然后让你求这些点有多少种组合可以形成共线的情况

当两个点在一个坐标上时这两个点可以看做是不同的两个点,也就是说如果两个点在一个坐标上,这两个点也算共线

我的这道题的解题思路就是先把每一个点的横坐标按从小到大排好序,然后从第一个点开始遍历,当遍历到一个点的时候,以这个点为起点,再向下遍历,

记录这个起点和以后每个点的向量,把这个向量化简到最小,用一个map保存起来

最后通过总共有多少个向量数来计算有多少种共线

我们可以先总结出一个规律,当有n个点共线时,组合个数为:2^n-n-1

由于我们在第一次遍历中已经确定了起点,所以每一次只会有1中多点在同一个坐标上的情况

向量分为两种

第一种是:(0,0)这样的向量代表有多个点是在一个坐标上,此时我们就要先计算出在一个点上有多少个情况共线

这样的向量有n个,就代表着有n+1个点,通过公式我们可以计算出多个点在一个坐标上时的组合个数,通过上面的公式:2^(n+1)-n-2

第二种是:向量中都不为0,此时组合的个数为:前面得到的在一个坐标上点的个数的组合数乘上向量个数的组合数,前面的在一个坐标上的数量为n1,当前向量个数为n2

通过公式可以得到:(2^n1-1)*(2^(n2+1)-1)

把这两种加起来就是以一个点为起点时的共线的组合个数,最后遍历全部再相加就是总的数量了

注意:本题要用long long否则无法通过!!!!!!!!!!!!!!!!!!!!

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<stdlib.h> 
#include<string.h>
#include<algorithm>
#include<stack>
#include<vector>
#include<queue>
#include<map>
#include<iterator>
#include<math.h>
#include<set>
using namespace std;
#define MAX 1000000007
long long gcd(long long x,long long y) //最大公约数的求值
{  
    if(y==0) return x;  
    else return(gcd(y,x%y));  
}   
long long mod_exp(long long a, long long b,long long c) //最大幂取余的模板,a^b%c
{
    long long res, t;
    res = 1 % c; 
    t = a % c;
    while (b)
    {
        if (b & 1)
        {
            res = res * t % c;
        }
        t = t * t % c;
        b /= 2 ;
    }
    return res;
}
struct Point //这是点的结构体,也是向量的结构体,其中需要对<进行重载,否则map无法通过编译
{
	long long x;long long y;
	bool operator<(const Point&x)const{
		if(this->x==x.x)
			return this->y<x.y;
		else
			return this->x<x.x;
	}
}pp[1100];
map<Point,long long> f; //这个是记录不同向量的个数的
map<Point,long long>::iterator it;//迭代器
map<Point,long long>g;//对在同一坐标上的点进行标记
bool cmp(Point x,Point y) 
{
	return x<y;

}
int main()
{
	long long t,n,sum,ans,index,i,j;
	scanf("%lld",&t);
	while(t--)
	{
		g.clear();
		f.clear();
		ans=0;
		scanf("%lld",&n);
		for(i=0;i<n;i++)
		{
			scanf("%lld %lld",&pp[i].x,&pp[i].y);
		}
		sort(pp,pp+n); //对每一个点进行排序
		for(i=0;i<n;i++)
		{
			
			if(g[pp[i]]) //如果当前的是以前判定过的在一个坐标上的点,则跳过这个点,因为已经计算过了
				continue;
			f.clear(); //清空map
			for(j=i+1;j<n;j++)
			{
				long long x=pp[j].x-pp[i].x;//得到向量
				long long y=pp[j].y-pp[i].y;
				if(x<0)//把向量调正
				{
					x=-x;
					y=-y;
				}
				long long GCD=gcd(x,abs(y));
				if(x!=0&&y!=0)//这个是如果向量的x或向量的y是0的话直接把另一个向量化为1
				{
					if(x==0)
						y=1;
					if(y==0)
						x=1;
				}
				if(GCD!=0) //把向量化简到x和y为互质的程度
				{
					x/=GCD;
					y/=GCD;
				}
				Point temp;
				temp.x=x;
				temp.y=y;
				f[temp]++; //记录向量的个数
			}
			long long k;
			Point ggg; //这个是在坐标一个点上的向量,因为在一个点上,所以他的x和y都为0
			ggg.x=0;
			ggg.y=0;
			long long temp;
			temp=f[ggg]; //temp为以当前点为起点在一个坐标上的向量的个数
			for(it=f.begin();it!=f.end();it++) //开始遍历map
			{
				if(it->first.x==0&&it->first.y==0) //如果是在一个点的向量,就是向量为(0,0),那么直接就算这些点有多少种组合
				{
					k=mod_exp(2,it->second+1,MAX);
					k=k-it->second-2;
					ans+=k;		
					ans%=MAX;
				}
				else //如果不是,则要计算组合的乘积
				{
					k=mod_exp(2,it->second,MAX);
					k=k-1;//这个k是在一个坐标上的点有多少种组合
					ans+=(k*(mod_exp(2,temp+1,MAX)-1));//然后把k和后面的点有多少种组合进行相乘
					ans%=MAX;
				}
			}
			g[pp[i]]=1;//把在一个坐标的点上进行标记,以后就不再看了
		}
		ans%=MAX;
		printf("%lld\n",ans);
	}
    return 0;
} 

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告