XXVIII.[TopCoder12693]EnclosingTriangle
经典套路是固定一个点,求出所有合法的剩余两个点。
为了方便,我们将环状的图形拆开,拆成 \(4n\) 个点。然后,我们枚举一个点 \(i\) 明显发现,剩下两个点必定位于 \(i\) 两侧的一端区间内,不妨设一半是 \([L_i,i)\),另一半是 \((i,R_i]\)。此部分可以通过求出当前点与全部二十个点间的向量中倾角最大以及最小的两个,并且求出它们与正方形的交点并取整得到,复杂度 \(O(mn)\)。
然后,考虑我们枚举第一个点 \(i\)。则第二个点必定在 \(\Big(i,\min(4m-1,R_i)\Big]\) 中,第三个点必在 \([L_i,4m-1]\) 中(因为要保证三个点递增)。于是,我们用two-pointers维护所有合法的第二个点 \(j\),并用线段树维护所有这样的 \(j\) 的 \((j,R_j]\) 上的带权和。之后,询问第三个点可能在的位置的带权和即可。复杂度 \(O(m\log m)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps=1e-10;
int cmp(double x){
if(x>eps)return 1;
if(x<-eps)return -1;
return 0;
}
class EnclosingTriangle{
private:
int n,m,L[400100],R[400100],N;
struct Vector{
double x,y;
Vector(){}
Vector(double X,double Y){x=X,y=Y;}
friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
friend Vector operator *(const Vector &u,const double &v){return Vector(u.x*v,u.y*v);}
friend Vector operator /(const Vector &u,const double &v){return Vector(u.x/v,u.y/v);}
friend double operator &(const Vector &u,const Vector &v){return u.x*v.y-u.y*v.x;}//cross times
friend double operator |(const Vector &u,const Vector &v){return u.x*v.x+u.y*v.y;}//point times
double operator ~()const{return sqrt(x*x+y*y);}//the modulo of a vector
double operator !()const{return atan2(y,x);}//the angle of a vector
void print(){printf("(%lf,%lf)",x,y);}
}p[30];
typedef Vector Point;
struct Line{
Point x,y;
Vector z;
Line(){}
Line(Point X,Point Y){x=X,y=Y,z=Y-X;}
friend Point operator &(const Line &u,const Line &v){return u.x+u.z*((v.z&(u.x-v.x))/(u.z&v.z));}
}bd[4];
typedef Line Segment;
int code(int x,int y){//code a point into an identification
if(y==0)return x;
if(x==m)return y+m;
if(y==m)return 3*m-x;
if(x==0)return 4*m-y;
}
int side(int x,int y){//find the side of a point
int ret=0;
if(y==0)ret|=1;
if(x==m)ret|=2;
if(y==m)ret|=4;
if(x==0)ret|=8;
return ret;
}
Point deco(int id){//deco an identification into a point
(id+=N)%=N;
if(id<m)return Point(id,0);
if(id<2*m)return Point(m,id-m);
if(id<3*m)return Point(3*m-id,m);
if(id<4*m)return Point(0,4*m-id);
}
int Crossover(Line l,int tp,int rd){//rd=1:round to maximal; rd=-1:round to minimal
if(!cmp(l.z&bd[tp].z))return -1;//two lines parallel
Point i=l&bd[tp];
if(cmp(i.x-m)==1||cmp(i.x)==-1||cmp(i.y-m)==1||cmp(i.y)==-1)return -1;//outside the boundary
int u,v;
if(tp==0){
u=i.x,v=u+1;
if(cmp(u-i.x)==0)return code(u,0);//coincide.
if(cmp(v-i.x)==0)return code(v,0);
return rd==1?code(v,0):code(u,0);
}
if(tp==1){
u=i.y,v=u+1;
if(cmp(u-i.y)==0)return code(m,u);
if(cmp(v-i.y)==0)return code(m,v);
return rd==1?code(m,v):code(m,u);
}
if(tp==2){
v=i.x,u=v+1;
if(cmp(u-i.x)==0)return code(u,m);
if(cmp(v-i.x)==0)return code(v,m);
return rd==1?code(v,m):code(u,m);
}
if(tp==3){
v=i.y,u=v+1;
if(cmp(u-i.y)==0)return code(0,u);
if(cmp(v-i.y)==0)return code(0,v);
return rd==1?code(0,v):code(0,u);
}
}
int findlimit(Line l,int rd,int ban){
for(int i=0;i<4;i++)if(!(ban&(1<<i))){
int tmp=Crossover(l,i,rd);
// printf("%d:%d\n",i,tmp);
if(tmp!=-1)return tmp;
}
}
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
struct SegTree{int tag;ll sum;}seg[1001000];
void ADD(int x,int y,int l,int r){seg[x].sum+=1ll*(r-l+1)*y,seg[x].tag+=y;}
void pushup(int x){seg[x].sum=seg[lson].sum+seg[rson].sum;}
void pushdown(int x,int l,int r){ADD(lson,seg[x].tag,l,mid),ADD(rson,seg[x].tag,mid+1,r),seg[x].tag=0;}
void modify(int x,int l,int r,int L,int R,int val){
if(l>R||r<L)return;
if(L<=l&&r<=R){ADD(x,val,l,r);return;}
pushdown(x,l,r),modify(lson,l,mid,L,R,val),modify(rson,mid+1,r,L,R,val),pushup(x);
}
ll query(int x,int l,int r,int L,int R){
if(l>R||r<L)return 0;
if(L<=l&&r<=R)return seg[x].sum;
pushdown(x,l,r);return query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
}
void modify(int x,int val){modify(1,0,N-1,x+1,R[x]<x?N-1:R[x],val);}
public:
ll getNumber(int M,vector<int>X,vector<int>Y){
m=M,n=X.size(),N=4*M;
bd[0]=Line(Vector(0,0),Vector(m,0));
bd[1]=Line(Vector(m,0),Vector(m,m));
bd[2]=Line(Vector(m,m),Vector(0,m));
bd[3]=Line(Vector(0,m),Vector(0,0));
for(int i=0;i<n;i++)p[i]=Vector(X[i],Y[i]);
for(int i=0;i<4*m;i++){
Point q=deco(i),mn=p[0],mx=p[0];
for(int j=1;j<n;j++){
if(cmp((p[j]-q)&(mn-q))==-1)mn=p[j];
if(cmp((p[j]-q)&(mx-q))==1)mx=p[j];
}
// q.print(),mn.print(),mx.print(),puts("");
L[i]=findlimit(Line(q,mn),1,side(q.x,q.y));
R[i]=findlimit(Line(q,mx),-1,side(q.x,q.y));
// printf("%d %d\n",L[i],R[i]);
}
ll res=0;
for(int i=0,j=1;i<N;){
if(L[i]<i)break;
while(j!=(R[i]+1)%N)modify(j,1),(++j)%=N;
res+=query(1,0,N-1,L[i],4*m-1);
// printf("%d\n",res);
modify(++i,-1);
}
return res;
}
}my;