HDU 5738 Eureka(贡献、极角排序)
题意:
$N\le 10^3个点,求有多少子集共线$
分析:
$首先合并重点,然后把点字典序排序,直线向量x就恒正了$
$枚举1个端点,对于所有字典序比它大的点极角排序一波$
$对于所有共线的点,就是左端点选一个非空子集,右端点选一个非空子集$
$所以贡献E_1=(2^{cnt_1}-1)\times (2^{cnt_2}-1)$
$当然重点自己也要算下贡献,E_2=2^{cnt}-1-cnt$
$这个就是选取2个端点(外面空0个点),然后外面空1个,2个点…里面随便选$
$E_2=2^{cnt-2}+2\times 2^{cnt-3}+3\times 2^{cnt-4}+\cdots+(cnt-2)\times 2^1+(cnt-1)$
$乘2错位一下,2E_2=2^{cnt-1}+2\times 2^{cnt-2}+3\times 2^{cnt-3}+(cnt-1)\times 2^1$
$一减,E_2=2^{cnt-1}+2^{cnt-2}+2^{cnt-3}+\cdots+2^1-(cnt-1)$
$等比数列求和一下,E_2=\frac{2\times (1-2^{cnt-1})}{1-2}-(cnt-1)=2^{cnt}-2-cnt+1=2^{cnt}-1-cnt$
$然后就做完了,时间复杂度,常数很小的O(n^2logn)$
代码:
//
// Created by TaoSama on 2016-07-22
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e3 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n;
struct Point {
int x, y;
void read() {scanf("%d%d", &x, &y);}
Point() {}
Point(int x, int y): x(x), y(y) {}
bool operator<(const Point& rhs) const {
return make_pair(x, y) < make_pair(rhs.x, rhs.y);
}
bool operator==(const Point& rhs) const {
return make_pair(x, y) == make_pair(rhs.x, rhs.y);
}
Point operator-(const Point& rhs) const {
return Point(x - rhs.x, y - rhs.y);
}
void reduce() {
int g = __gcd(abs(x), abs(y));
if(g) x /= g, y /= g;
}
} p[N];
int two[N] = {1};
void add(int& x, int y) {
if((x += y) >= MOD) x -= MOD;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
clock_t _ = clock();
for(int i = 1; i < N; ++i) two[i] = 2 * two[i - 1] % MOD;
int t; scanf("%d", &t);
while(t--) {
scanf("%d", &n);
map<Point, int> mp;
for(int i = 1; i <= n; ++i) {
p[i].read();
++mp[p[i]];
}
vector<pair<Point, int> > cnt(mp.begin(), mp.end());
int ans = 0;
for(int i = 0; i < cnt.size(); ++i) {
Point p; int c1;
tie(p, c1) = cnt[i];
vector<pair<Point, int> > v;
for(int j = i + 1; j < cnt.size(); ++j) {
Point np; int c2;
tie(np, c2) = cnt[j];
Point o = np - p; o.reduce();
v.push_back({o, c2});
}
sort(v.begin(), v.end());
add(ans, two[c1] - 1 - c1); //x points is collinear
for(int x = 0, y; x < v.size(); x = y) {
int c2 = 0;
for(y = x; y < v.size(); ++y) {
if(v[x].first == v[y].first) c2 += v[y].second;
else break;
}
int e = 1LL * (two[c1] - 1) * (two[c2] - 1) % MOD;
add(ans, e);
}
}
printf("%d\n", ans);
}
#ifdef LOCAL
printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
return 0;
}