「CEOI2008」Dominance 题解
废话
\(~~~~\) 成功抢到一血,发篇题解纪念一下(顺便吐槽一下国内居然都没有这题题解,就只有CEOI官方有英文)
\(~~~~\) 所以这篇题解基本以搬运为主
题意
\(~~~~\) 给出 \(n\) (\(1\leq n\leq 3000\)) 个坐标及其影响范围 \(d\) ,它可以影响距离它曼哈顿距离 \(\leq d\) 的所有整点(坐标均为整数的点),同时每种影响分为黑色和白色。最后某个整点受到影响最多的颜色即为该点的颜色,求最后白点、黑点分别有多少个。
\(~~~~\) 坐标系最远的坐标为 \(10^9\) 。
题解
\(~~~~\) 一开始看到这道题以为很简单,但考虑到坐标系大小直接放弃。
\(~~~~\) 但有一部分思路是可以沿用的。首先,由于曼哈顿距离的影响范围是斜放的正方形,显然很难用数据结构维护斜着的坐标系,因此我们把它转为切比雪夫距离。这部分的具体见 洛谷日报#182 常用距离算法详解 。这里仅提一下结论:
\(~~~~\) 将坐标 \((x,y)\) 变为 \((x+y,x-y)\) (或 \((x-y,x+y)\) 亦可,本文选用前者 ),则原先点之间的曼哈顿距离与变换后点之间的切比雪夫距离是相等的。
\(~~~~\) 这样做就可以把正方形的四边变为平行于坐标轴的线段,方便之后的维护。但要注意,变换后坐标系内的某些整点不一定有原先的整点与之对应。更具体来看,我们可以把 还原 这个过程视作解二元一次方程,则变换后坐标 \((x,y)\) 是由 \((\frac{x+y}{2},\frac{x-y}{2})\) 变换而来的,由此我们可以知道 变换后坐标之和为偶数的点有原先整点与之对应,进而把变换后的点分为两类:横纵坐标均为奇数 和 横纵坐标均为偶数 。
\(~~~~\) 下一部分,考虑之前我们为什么觉得困难,是因为坐标系过大,因此我们必须转换视角,从每个点上入手。看到一堆矩形可以自然而然地想到扫描线。 由于在复杂度不与坐标轴大小相关的情况下必须对点进行离散化,而本题离散化后处理并不方便,因此我们选择不用离散化的扫描线。
\(~~~~\) 若不考虑坐标轴内有无用点(即不能被统计进入的点)的话,这的确是一道扫描线裸题,那么考虑用上文提到的将点分为两类,分别维护横纵线上的奇线和偶线则可维护矩形内分别有多少奇点和偶点。
代码
查看代码
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
ll Coord[6005],Tag[6005];
char Col[5];
struct Square{
ll odd,even;
Square(){}
Square(ll O,ll E){odd=O,even=E;}
#define Square_OPERATOR(op) Square operator op (const Square &t){return Square(odd op t.odd,even op t.even);}//对应相加、乘
Square_OPERATOR(+=);
Square_OPERATOR(-=);
Square_OPERATOR(*);
}W,B,AnsW,AnsB,Squ;
struct UpRight{
ll x,l,r,Type;
UpRight(){}
UpRight(ll X,ll L,ll R,ll T){x=X,l=L,r=R,Type=T;}
}Up[6005];
bool cmpUp(UpRight x,UpRight y){return x.x<y.x;}
Square Get(ll l,ll r)//一维线
{
Square ret=Square((r-l)>>1,(r-l)>>1);
if((r-l)&1) (r&1)?ret.odd++:ret.even++;
return ret;
}
int main() {
ll n,m,p;
scanf("%lld %lld %lld",&n,&m,&p);
ll U=0,C=0;
for(ll i=1,x,y,d;i<=p;i++)
{
scanf("%s %lld %lld %lld",Col+1,&x,&y,&d);
x=x+y; y=x-2*y;
ll Type=((Col[1]=='W')?1:-1);
Up[++U]=UpRight(x-d-1,y-d-1,y+d,Type);
Up[++U]=UpRight(x+d, y-d-1,y+d,-Type);
Coord[++C]=y-d-1;
Coord[++C]=y+d;
}
sort(Up+1,Up+1+U,cmpUp);
sort(Coord+1,Coord+1+C); C=unique(Coord+1,Coord+1+C)-Coord-1;
ll Last=Up[1].x;
for(ll i=1;i<=U;i++)
{
Squ=Get(Last,Up[i].x);
AnsW+=Squ*W;
AnsB+=Squ*B;
Last=Up[i].x;
ll l,r,col=Up[i].Type;
l=lower_bound(Coord+1,Coord+1+C,Up[i].l)-Coord+1;
r=lower_bound(Coord+1,Coord+1+C,Up[i].r)-Coord;
for(ll j=l;j<=r;j++)
{
Squ=Get(Coord[j-1],Coord[j]);
if(Tag[j]==0&&col==1) W+=Squ;
if(Tag[j]==0&&col==-1) B+=Squ;
if(Tag[j]==1&&col==-1) W-=Squ;
if(Tag[j]==-1&&col==1) B-=Squ;
Tag[j]+=col;
}
}
printf("%lld %lld\n",AnsW.odd+AnsW.even,AnsB.odd+AnsB.even);
return 0;
}