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;
}