题目大意:
平面上有n个点,其中任意2点不重合,任意3点不共线。我们等概率地选取一个点A,再在剩下的n-1个点中等概率地选取一个点B,再在剩下的n-2个点中等概率地选取一个点C。然后我们计算伤害倍率d。作ABC外接圆,每一个位于弧BC和线段BC之间的点计1倍,每一个位于弧BC上的点(包括B,C两点)计1/2倍,特别的,点A计1倍。将这些倍率全部加起来得到伤害倍率d。注意:弧BC是指,ABC外接圆上B到C而不包含点A的部分,这个弧不一定是劣弧。给定这n个点的坐标,你需要求出d的期望。为了简单起见,你只需要输出
的值,可以看出这是一个整数。
题解:
考虑每次ABC这三个点对答案的贡献总和为
单独计算,剩下相当于是对每个三角形画个外接圆然后再看一个点是否在弧和弦之间或在弧上。
发现D在ABC(A和D相对)的外接圆弧和弦之间当且仅当,ABCD构成凸四边形且
。特殊的当
时四点共圆,任选其中三点,另外一个点一定在弧上,对答案总贡献
。否则
,那么A和D分别剩下三个点的外接圆的弧和弦之间,B和C则不在,因此对答案贡献也是
。
因此问题转化为统计凸四边形个数(非凸四边形任取三点都无贡献)。
考虑一个四边形四个顶点两两连边,凸四边形有四种情况,剩下两个点在同侧,两种情况在异侧;非凸四边形则分别有三种情况。
因此设有
个凸四边形,
非凸四边形,枚举点对统计多少剩下两点在异侧/同侧方案数,记作
,则
,最后答案时
。
(后来改了一发为了避免被卡atan2精度导致代码有点奇妙(大雾
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define db long double
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn() { int x;scanf("%d",&x);return x; }
const int N=1010;
struct P{
lint x,y;int qd;P(lint _x=0,lint _y=0) { x=_x,y=_y; }
inline int getqd()
{
if(x>0&&y==0) qd=0;if(x>0&&y>0) qd=1;if(x==0&&y>0) qd=2;if(x<0&&y>0) qd=3;
if(x<0&&y==0) qd=4;if(x<0&&y<0) qd=5;if(x==0&&y<0) qd=6;if(x>0&&y<0) qd=7;
return 0;
}
inline P operator-(const P &p)const { return P(x-p.x,y-p.y); }
inline db cross(const P &p)const { return (db)x*p.y-(db)y*p.x; }
inline bool operator<(const P &p)const { if(qd^p.qd) return qd<p.qd;return cross(p)>0; }
}p[N];
namespace SOLVE_space{
P p[N];int n;inline int nxt(int x) { return x++,(x>n?1:x); }
inline int solve(P *_p,int _n,const P &s,lint &X,lint &Y)
{
n=_n;rep(i,1,n) p[i]=_p[i]-s,p[i].getqd();sort(p+1,p+n+1);
for(int i=1,j=1,cnt=0;i<=n;i++,cnt--)
{
if(cnt<0) j=i,cnt=0;while(nxt(j)!=i&&p[i].cross(p[nxt(j)])>=0) j=nxt(j),cnt++;
X+=cnt*(n-cnt-1ll),Y+=cnt*(cnt-1ll)/2+(n-cnt-1ll)*(n-cnt-2)/2;
}
return 0;
}
}using SOLVE_space::solve;
int main()
{
int n=inn();lint X=0,Y=0;
rep(i,1,n) p[i].x=inn(),p[i].y=inn();
rep(i,1,n) swap(p[n],p[i]),solve(p,n-1,p[n],X,Y),swap(p[n],p[i]);
X/=2,Y/=2;lint a=(Y-X)/2,ans=4*n*(n-1ll)*(n-2ll)+a*8;
return !printf("%lld\n",ans);
}