15-16 ICPC europe J Saint John Festival (graham扫描法+旋转卡壳)
题意:给n个大点,m个小点$(n<=1e5,m<=5e5),问有多少个小点,存在3个大点,使小点在三个大点组成的三角形内。
解题思路:
首先,易证,若该小点在某三大点行成的三角形内,则该小点必然处在大点组成的凸包内。那么首先,现将大点形成的凸包计算出来,但是若对每个点进行暴力枚举,在最坏情况下,若凸包上点数太多并且脸黑不断找区间,复杂度会达到$O(nm)$,因此要对点是否在凸包内的判断过程进行优化。
将凸包划分为若干个三角形,我们可以先将这m个待判断的点进行极角排序,排序后,极角较大的点要么处在较小的点的逆时针方向的三角形内要么在同一个三角形内,要么两个点在同一三角形内。因此在点坐标转移时,采用旋转卡壳,所在的三角形区域也逆时针选择即可。而对于小点是否在三角形内,采用面积判断即可,为了防止爆精度推荐用行列式计算面积,时间复杂度$O(nlogn+mlogm+m)$
下面贴上AC代码。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<ll,ll> pii; #define rep(i,a,b) for(int i=a;i<b;i++) #define rept(i,a,b) for(int i=a;i<=b;i++) #define mes(a,b) memset(a,b,sizeof a) #define pb push_back #define dd(x) cout<<#x<<"="<<x<<" " #define de(x) cout<<#x<<"="<<x<<"\n" #define fi first #define se second #define mp make_pair pii point[10005],small[50005]; pii operator -(const pii &s1,const pii &s2) { return mp(s1.fi-s2.fi,s1.se-s2.se); } int s[10005]; ll chaji(const pii &s1,const pii &s2) { return s1.fi*s2.se-s1.se*s2.fi; } bool comp(const pii &s1,const pii &s2) { if((s1.se<point[0].se)+(s2.se<point[0].se)==1) return s1.se>s2.se; ll x=chaji(s1-point[0],s2-point[0]); if(x>0||(x==0&&abs(s1.fi-point[0].fi)<abs(s2.fi-point[0].fi))) return 1; else return 0; } ll sqare(const pii &a,const pii &b,const pii &c) { return abs( (b.fi-a.fi)*(c.se-a.se) -(c.fi-a.fi)*(b.se-a.se) ); } int main() { ios::sync_with_stdio(false); cin.tie(0); int n,m; cin>>n; rep(i,0,n) cin>>point[i].fi>>point[i].se; int p=0; rep(i,1,n) if( point[i].se<point[p].se||(point[i].se==point[p].se&&point[i].fi<point[p].fi) ) p=i; swap(point[0],point[p]); //dd(point[0].fi);de(point[0].se); sort(point+1,point+n,comp); int cnt=0; s[cnt++]=0; s[cnt++]=1; rep(i,2,n) { while(cnt>=2&&chaji(point[s[cnt-1]]-point[i],point[s[cnt-2]]-point[i])>=0) cnt--; s[cnt++]=i; } cin>>m; rep(i,0,m) cin>>small[i].fi>>small[i].se; sort(small,small+m,comp); int ans=0; p=1; int start=0; while(start<m&&chaji(small[start]-point[s[0]],point[s[1]]-point[s[0]])>0) start++; rep(i,start,m) { if(small[i].se<point[0].se) break; while(p<cnt-1&&chaji( small[i]-point[s[0]],point[s[p+1]]-point[s[0]] )<0 ) p++; if(p==cnt-1) break; if(chaji( small[i]-point[s[0]],point[s[p]]-point[s[0]] )==0 ) { if((small[i].fi<point[s[0]].fi)+(small[i].fi<point[s[p]].fi)==1) ans++; continue; } else if(chaji( small[i]-point[s[0]],point[s[p+1]]-point[s[0]] )<0) { if((small[i].fi<point[s[0]].fi)+(small[i].fi<point[s[p+1]].fi)==1) ans++; continue; } if(sqare(point[s[0]],point[s[p]],point[s[p+1]])==sqare(point[s[0]],point[s[p]],small[i])+sqare(point[s[0]],point[s[p+1]],small[i])+sqare(point[s[p]],point[s[p+1]],small[i])) ans++; } cout<<ans<<endl; return 0; }