LOJ6049 拍苍蝇
拍苍蝇
有一天,小A的母亲对他家里的卫生状况非常不满意,他的房间里有非常多的苍蝇。在母亲的威逼利诱下,小A拿起了苍蝇拍去消灭家里的苍蝇。然而,小A以前从来没有亲手消灭过任何一只苍蝇,以至于在他拿起苍蝇拍的那一刻,他对苍蝇起了怜悯之心 —— 他不想伤害任何一只苍蝇。现在,小A面前的窗户上有\(N\)只苍蝇,他想知道有多少种方式可以在他拿起苍蝇拍拍窗户的时候,不伤害任何一只苍蝇。
窗户可以看作是一个左下角位于坐标系原点的矩形,苍蝇拍可以看作是一个多边形。在小A向窗户挥苍蝇拍时,对应多边形的顶点必须位于坐标系的整点上,并且苍蝇拍所在位置不能超过窗户所在范围。一只苍蝇会被伤害当且仅当小A用苍蝇拍拍向窗户时,苍蝇位于苍蝇拍内部、边上或顶点上。
题解
考虑bitset。如果我们能求出苍蝇拍上有哪些整点,那么直接枚举苍蝇拍放的位置然后判定整点跟苍蝇无交即可。时间复杂度\(O(\frac{X^4}{64})\)。
问题转化成如何求苍蝇拍上的整点,这是个经典扫描线问题。由于数据范围很小,所以我们可以暴力枚举横坐标,然后对纵坐标进行排序。每经过一条边,在内部/在外部的状态就会翻转。注意特判多边形的边竖直的情况。时间复杂度\(O(XK\log K)\),当然这是跑不满的。
CO int N=510,M=1e5+10;
struct point {int x,y;} p[M];
bitset<N> fly[N],swat[N];
int val[N][N];
int main(){
int Xp=read<int>(),Yp=read<int>();
for(int n=read<int>();n--;){
int x=read<int>(),y=read<int>();
fly[x][y]=1;
}
int K=read<int>();
int Xl=1e9,Xr=-1e9,Yl=1e9,Yr=-1e9;
for(int i=1;i<=K;++i){
read(p[i].x),read(p[i].y);
Xl=min(Xl,p[i].x),Xr=max(Xr,p[i].x),Yl=min(Yl,p[i].y),Yr=max(Yr,p[i].y);
}
Xr-=Xl,Yr-=Yl;
for(int i=1;i<=K;++i){
p[i].x-=Xl,p[i].y-=Yl;
swat[p[i].x][p[i].y]=1;
}
for(int x=0;x<=Xr;++x){
vector<float128> stk;
for(int i=1;i<=K;++i){
point a=p[i],b=p[i%K+1];
if(a.y>b.y) swap(a,b);
if((a.x<x and b.x<x) or (a.x>x and b.x>x)) continue;
if((a.x==x and b.x>x) or (b.x==x and a.x>x)) continue; // count it only once
if(a.x==x and b.x==x){
for(int y=a.y+1;y<=b.y-1;++y) swat[x][y]=1;
continue;
}
stk.push_back((float128)(b.y-a.y)/(b.x-a.x)*(x-a.x)+a.y);
}
sort(stk.begin(),stk.end());
for(int y=0,i=0,flag=0;y<=Yr;++y){
for(;i<(int)stk.size() and stk[i]<y;++i) flag^=1; // inside or outside
if(flag or (i<(int)stk.size() and stk[i]==y)) swat[x][y]=1;
}
}
for(int x=0;x<=Xp-Xr;++x) fill(val[x],val[x]+Yp-Yr+1,1);
for(int i=0;i<=Xr;++i)for(int y=0;y<=Yp-Yr;++y){
CO bitset<N>&now=swat[i]<<y;
for(int x=0;x<=Xp-Xr;++x)
if(val[x][y] and (now&fly[x+i]).any()) val[x][y]=0;
}
int ans=0;
for(int x=0;x<=Xp-Xr;++x) ans+=accumulate(val[x],val[x]+Yp-Yr+1,0);
printf("%d\n",ans);
return 0;
}
静渊以有谋,疏通而知事。