【HDU5738 2016 Multi-University Training Contest 2E】【平面點數計數 共線判定】Eureka 平面有多少個集合滿足貢獻

snowy_smile發表於2016-07-28

Eureka

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


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

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1010, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n;
struct A
{
	int x, y;
	bool operator < (const A&b)const
	{
		if (x != b.x)return x < b.x;
		return y < b.y;
	}
}a[N], b[N];
int bit[N];
int gcd(int x, int y)
{
	return y == 0 ? x : gcd(y, x%y);
}
void add(int &x, int y)
{
	if ((x += y) >= Z)x -= Z;
}
void datamaker()
{
	freopen("c://test//input.in", "w", stdout);
	int T = 100; printf("%d\n", T);
	while (T--)
	{
		int n = rand() % 4 + 1; printf("%d\n", n);
		for (int i = 1; i <= n; ++i)
		{
			int x = rand() % 5 + 1;
			int y = rand() % 5 + 1;
			printf("%d %d\n", x, y);
		}
	}
}
int main()
{
	//datamaker(); return 0;
	//fre();
	bit[0] = 1; for (int i = 1; i <= 1000; i++) bit[i] = (bit[i - 1] << 1) % Z;
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) scanf("%d%d", &a[i].x, &a[i].y);
		sort(a + 1, a + n + 1);
		int ans = 0;
		for (int i = 1; i <= n; ++i)
		{
			int same = 0;
			int g = 0;
			for (int j = i + 1; j <= n; ++j)
			{
				if (a[j].x == a[i].x && a[j].y == a[i].y)
				{
					add(ans, bit[same++]);
				}
				else
				{
					int x = a[j].x - a[i].x;
					int y = a[j].y - a[i].y;
					int g_ = gcd(x, y);
					b[++g] = { x / g_, y / g_ };
				}
			}
			sort(b + 1, b + g + 1);
			int line = 0;
			for (int j = 1; j <= g; ++j)
			{
				if (b[j].x != b[j - 1].x || b[j].y != b[j - 1].y)line = 0;
				add(ans, (LL)bit[same] * bit[line++] % Z);
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}
/*
【trick&&吐槽】
讀題是AC的基礎啊。
這道題不光做法常數大TLE了,讀題轉化也出了問題。
= =#我是該感到開心還是難過呢>.<

1,固定一個點的思路特別棒。
2,來一個點就算這個點的貢獻的做法,十分精妙而優美。

【題意】
平面上有n個點,
問你有多少個點集滿足——
點集上至少有2個點,使得該集合的其他點都在這2點所連線段的區間範圍內。
(這是轉化後的問題)

【型別】
平面點數計數

【分析】
我們計數的時候如何使得不重複計數呢?
一個很值得采納的做法,是列舉、固定一個點。

這道題就是這個樣子——
我們可以列舉一個點,這個點一定在集合內,然後再考慮其它點的相關性。

1,如果只涉及關於重點的集合,顯然對答案的貢獻是2^(與之成為重點的點個數)-1
2,如果還涉及非重點的集合,對答案的貢獻是(2^(與之成為重點的點個數))*(2^(共線點數)-1)

【時間複雜度&&優化】
O(n^2logn)

*/


相關文章