[CF372E]Drawing circles is fun(反演)

题面

http://codeforces.com/problemset/problem/372/E

题解

前置知识

暂且称原题中的\(P_{2i-1}\)\(P_i\)\(P_{2i}\)\(Q_i\)

先澄清一下题意,我认为有一点原题的表述不够清楚。就是原题求的是点对集合\(((P_1,Q_1),(P_2,Q_2),…,(P_k,Q_k))\)的个数。集合中的所有点对当然是无序的,但是按照我们对“点对”的理解,每个点对中间的两个点应该是有序的。但是经过对此题样例的分析,本题表达的意思中,点对中的两个点也是无序的。即,\(((A,B),(C,D))\)\(((B,A),(C,D))\)算作一组解。

以原点作为反演中心,1作为反演幂(只是推荐,因为本题给出的坐标的绝对值最大为50,最小为\(\frac{1}{50}\),所以做一个几何平均可以使得数据范围基本不变)对原图进行反演。

两个过反演中心的圆在反演中心处相切,等价于反演后是两条相互平行的直线。对原图反演后,\(P_{i}P_j // Q_iQ_j,P_iQ_j // Q_iP_j\)。也就等价于说\(P_iP_jQ_iQ_j\)形成平行四边形。根据平行四边形的判定,这等价于\(P_iQ_i\)\(P_jQ_j\)的中点重合,且\(P_iQ_i\)\(P_jQ_j\)不共线。

所以,将原图反演后,我们枚举所有的线段,先按线段的中点将它们分大类;然后在各大类中,再按它们的极角再分小类。然后就转化成一个组合计数问题,对于每一大类分别考虑,其中的每一小类要么不选,要么选一条线段,并且选的线段总数\({\geq}2\),求方案数。过程中,枚举线段时需要防止重复(即澄清题意部分所指的重复)。

这个问题可以使用总量(各”小类所含线段数+1“之积)减去总数0的方案(1)再减去总数1的方案(各”小类的所含线段数“之和)解决。然后对所有的大类求此答案的和即可解决。

时间复杂度\(O(n^2 \log n)\)。(log来自于分类的排序过程)

代码

#include<bits/stdc++.h>

using namespace std;

#define N 1000
#define rg register
#define In inline
#define ll long long
#define eps 1e-9

const ll mod = (ll)1e9 + 7;

In ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < ‘0‘ || ch > ‘9‘){if(ch == ‘-‘)ww = -1;ch = getchar();}
	while(‘0‘ <= ch && ch <= ‘9‘){s = 10 * s + ch - ‘0‘;ch = getchar();}
	return s * ww;
}

namespace ModCalc{
	In void Inc(ll &x,ll y){
		x += y;if(x >= mod)x -= mod;
	}
	In void Dec(ll &x,ll y){
		x -= y;if(x < 0)x += mod;
	}
	In ll Add(ll x,ll y){
		Inc(x,y);return x;
	}
	In ll Sub(ll x,ll y){
		Dec(x,y);return x;
	}
}
using namespace ModCalc;

In int sgn(double x){
	return x < -eps ? -1 : x > eps;
}

struct vec{
	double x,y;
	vec(){}
	vec(double _x,double _y){x = _x,y = _y;}
	In friend vec operator + (vec a,vec b){
		return vec(a.x + b.x,a.y + b.y);
	}
	In friend vec operator - (vec a,vec b){
		return vec(a.x - b.x,a.y - b.y);
	}
	In friend vec operator * (vec a,double k){
		return vec(a.x * k,a.y * k);
	}
	In friend vec operator / (vec a,double k){
		return vec(a.x / k,a.y / k);
	}
	In friend double Dot(vec a,vec b){
		return a.x * a.x + b.y * b.y;
	}
	In friend double Cross(vec a,vec b){
		return a.x * b.y - a.y * b.x;
	}
	In friend bool equ(vec a,vec b){
		return sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0;
	}
}p[N+5];

struct line{
	vec m,v;
	line(){}
	line(vec _m,vec _v){m = _m,v = _v;}
}l[N*N+5];
int ln;

In vec Adjust(vec a){
	int k;
	if((k = sgn(a.x)) < 0)a.x = -a.x,a.y = -a.y;
	else if(k == 0){
		if(sgn(a.y) > 0)a.y = -a.y;
	}
	return a;
}

In bool cmp(line a,line b){
	int k;
	if((k = sgn(a.m.x-b.m.x)) != 0)return k < 0;
	if((k = sgn(a.m.y-b.m.y)) != 0)return k < 0;
	return sgn(Cross(a.v,b.v)) == 1;
}

ll ans,n;
ll a[N*N+5],an;

void Count(){
	ll cur = 1;
	for(rg int i = 1;i <= an;i++)cur = cur * (a[i] + 1) % mod;
	Dec(cur,1);
	for(rg int i = 1;i <= an;i++)Dec(cur,a[i]);
	Inc(ans,cur);
	an = 0;
}

int main(){
//	freopen("CF372E.in","r",stdin);
//	freopen("CF372E.out","w",stdout);
	n = read();
	for(rg int i = 1;i <= n;i++){
		int a = read(),b = read(),c = read(),d = read();
		double x = (double)a / b,y = (double)c / d;
		double k = 1 / (x * x + y * y);
		p[i] = vec(x,y) * k;
	}
	for(rg int i = 1;i < n;i++){
		for(rg int j = i + 1;j <= n;j++){
			ln++;
			l[ln].m = (p[i] + p[j]) / 2;
			l[ln].v = Adjust(p[i] - p[j]);
		}
	}
	sort(l + 1,l + ln + 1,cmp);
	for(rg int i = 1;i <= ln;i++){
		if(i == 1 || !equ(l[i].m,l[i-1].m)){
			if(i != 1)Count();
			a[++an] = 1;
		}
		else if(sgn(Cross(l[i].v,l[i-1].v)) == 0)a[an]++;
		else a[++an] = 1;
	}
	Count();
	cout << ans << endl;
	return 0;
}

[CF372E]Drawing circles is fun(反演)

上一篇:delphi中的strTodate()


下一篇:Window 配置 Redis