JOI春合宿2014 K 二人の星座

JOI春合宿2014 K 二人の星座

链接

二人の星座

题意

平面内有n(n<=3000)个点,有三种颜色,每个点的颜色是三种中的一种,不存在三点一线。求不共用点且不相交的三色三角形对数。

题解

两个没有相交的三角形,一定能做出两条公切线,比较类似于两个圆的公切线。
考虑到这个公切线一定经过两个三角形各一个点,可以枚举这两个点。
然后用这条线把点分成两边,就能算出被这条公切线分开的三角形有几对了,最后由于每对三角形会被记4次,所以还要除以4。
然而这样做的话是(O(n^3))的。
先枚举一个点(i),将剩余点极角排序,就可以用双指针快速统计每条经过点(i)的公切线对答案的贡献了。
最后效率是(O(n^2logn))

(Code)

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e3+10;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
int n; 
struct P{
	int x,y;
	int c,id;
}a[N<<1];
P operator - (P x,P y){
	P z;
	z.x=x.x-y.x;
	z.y=x.y-y.y;
	return z;
}
LL cha(P x,P y){
	return (LL)x.x*y.y-(LL)x.y*y.x;
}
bool cmp(P x,P y){
	return atan2(x.y-a[0].y,x.x-a[0].x)<atan2(y.y-a[0].y,y.x-a[0].x);
}
LL ans=0;
LL L[3],R[3];
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;++i){
		a[i].x=read();
		a[i].y=read();
		a[i].c=read();
		a[i].id=i+1;
	}
	LL A,B,C,D;
	for(int i=1;i<=n;++i){
		int j=0;
		for(int k=0;k<n;++k){
			if(a[k].id==i){
				j=k;break;
			}
		}
		if(j) swap(a[0],a[j]);
		sort(a+1,a+n,cmp);
		L[1]=L[2]=L[0]=0;R[1]=R[2]=R[0]=0;
		for(int k=n;k<=n+n-2;++k) a[k]=a[k-n+1];
		int l=1,r=1;
		for(int k=1;k<n;++k){
			if(r<k)r=k;
			if(l<k)l=k;
			while(r+2<n+k){
				++r;
				++R[a[r].c];
			}
			while(l+2<n+k&&cha(a[k]-a[0],a[l+1]-a[0])>0){
				++l;
				--R[a[l].c];
				++L[a[l].c];
			}
			if(a[0].c==0){A=L[1]*L[2];B=R[1]*R[2];}
			else if(a[0].c==1){A=L[0]*L[2];B=R[0]*R[2];}
			else {A=L[0]*L[1];B=R[0]*R[1];}
			if(a[k].c==0){C=L[1]*L[2];D=R[1]*R[2];}
			else if(a[k].c==1){C=L[0]*L[2];D=R[0]*R[2];}
			else {C=L[0]*L[1];D=R[0]*R[1];}
			ans+=A*D+B*C;
			if(cha(a[k]-a[0],a[k+1]-a[0])>0) --L[a[k+1].c];
			else --R[a[k+1].c];
		}
	}
	ans=ans/(LL)4;
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/Yuigahama/p/13752203.html