据说是合宿系列最难的一题。。。(反正我一题都不会也没什么感觉)
考虑任意一对符合条件的三角形(i,j),一定可以在三角形i和j中找到两对点,这两点的连线段所在直线将这对三角形分割到两个不同的半平面中。即存在两条公切线。
那么我们就可以枚举这条公切线了,也就是O(N^2)枚举,这样可以暴力O(N^3)得到答案;注意到如果,枚举一个定点,然后将剩下的点按与定点连线段所在直线的斜率(极角)排序,那么就可以O(N)扫一遍得到答案了。那么就是O(N^2)的了。注意到一对三角形被重复算了4遍,最后要除以4。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define pi acos(-1.0) #define ll long long using namespace std; int n,c[2][3],blg[10005]; struct point{ int x,y,c,id; double k; }p[10005]; bool cmp(point x,point y){ return x.k<y.k; } int main(){ scanf("%d",&n); int i,j,k; for (i=1; i<=n; i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].c); for (i=1; i<=n; i++) p[i].id=i; point o; ll tmp,ans=0; for (i=1; i<=n; i++){ for (j=1; j<=n; j++) if (p[j].id==i) break; o=p[j]; k=o.c; for (j=1; j<=n; j++){ p[j].k=(p[j].id!=i)?atan2(p[j].y-o.y,p[j].x-o.x):1e100; if (p[j].k<=0) p[j].k+=pi; } sort(p+1,p+n+1,cmp); memset(c,0,sizeof(c)); for (j=1; j<n; j++) if (p[j].y<o.y || p[j].y==o.y && p[j].x>o.x){ c[0][p[j].c]++; blg[j]=0; } else{ c[1][p[j].c]++; blg[j]=1; } for (j=1; j<n; j++){ c[blg[j]][p[j].c]--; tmp=1; if (k) tmp*=c[0][0]; if (p[j].c) tmp*=c[1][0]; if (k^1) tmp*=c[0][1]; if (p[j].c^1) tmp*=c[1][1]; if (k^2) tmp*=c[0][2]; if (p[j].c^2) tmp*=c[1][2]; ans+=tmp; tmp=1; if (k) tmp*=c[1][0]; if (p[j].c) tmp*=c[0][0]; if (k^1) tmp*=c[1][1]; if (p[j].c^1) tmp*=c[0][1]; if (k^2) tmp*=c[1][2]; if (p[j].c^2) tmp*=c[0][2]; ans+=tmp; blg[j]^=1; c[blg[j]][p[j].c]++; } } printf("%lld\n",ans>>2); return 0; }
by lych
2016.3.28