CCPC2023 河南省赛 数正方形
数正方形
给出一个 (2n + 1) × (2n + 1) 的网格图,左下角的坐标为 (0, 0),右上角的坐标为 (2n + 1, 2n + 1)。
在这个网格图中有 n 个矩形,第 i 个矩形左下角的坐标为 (x1i, y1i),右上角的坐标为 (x2i, y2i),且序列{x11, x12, . . . , x1n, x21, x22, . . . , x2n} 和序列 {y11, y12, . . . , y1n, y21, y22, . . . , y2n} 分别都是 1 ∼ 2n 的一个排列。
我们按照以下方法对这 (2n + 1) × (2n + 1) 个格子进行黑白染色:被矩形覆盖了偶数次的格子染上白色,否则染上黑色。
求格子中有多少个 2 × 2 的正方形是纯色的,即仅由白色格子或黑色格子构成的正方形。
\(1 ≤ n ≤ 10^5\)
题解
按照方块纵坐标组合是(1, 2), (2, 3), ...的顺序统计纯色方块个数。
这个线段树操作比较有意思。扫描线上需要翻转的部分就翻转,不需要翻转的部分是可能产生纯色正方形的,需要统计相同颜色段的长度-1的和,可以用线段树维护。
上下两头需要特殊处理。
时间复杂度O(n log n)
constexpr int N=2e5+10;
vector<pair<int, int> > e[N];
struct Node{
bool l, r;
int sum;
};
Node operator+(const Node& a, const Node& b){
return {a.l, b.r, a.sum+b.sum+(a.r==b.l)};
}
Node operator~(const Node& a){
return {!a.l, !a.r, a.sum};
}
Node tree[4*N];
bool tag[4*N];
#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
void pushDown(int x){
if(tag[x]){
tree[lc]=~tree[lc], tag[lc]^=1;
tree[rc]=~tree[rc], tag[rc]^=1;
tag[x]=0;
}
}
void build(int x, int l, int r){
if(l==r){
tree[x]={0, 0, 0}, tag[x]=0;
return;
}
build(lc, l, mid);
build(rc, mid+1, r);
tree[x]=tree[lc]+tree[rc], tag[x]=0;
}
void modify(int x, int l, int r, int ql, int qr){
if(ql<=l && r<=qr){
tree[x]=~tree[x], tag[x]^=1;
return;
}
pushDown(x);
if(ql<=mid) modify(lc, l, mid, ql, qr);
if(qr>mid) modify(rc, mid+1, r, ql, qr);
tree[x]=tree[lc]+tree[rc];
}
Node query(int x, int l, int r, int ql, int qr){
if(ql<=l && r<=qr) return tree[x];
pushDown(x);
if(qr<=mid) return query(lc, l, mid, ql, qr);
if(ql>mid) return query(rc, mid+1, r, ql, qr);
return query(lc, l, mid, ql, qr)+query(rc, mid+1, r, ql, qr);
}
int main(){
int n=read<int>();
for(int i=1; i<=n; ++i){
int x1=read<int>(), y1=read<int>(), x2=read<int>(), y2=read<int>();
e[x1].push_back({y1, y2});
e[x2].push_back({y1, y2});
}
build(1, 1, 2*n-1);
int64 ans=0;
for(int p=1; p<=2*n; ++p){
vector<pair<int, int> > a;
bool top=0, bottom=0;
for(const pair<int, int>& t: e[p]){
a.push_back({t.first, 1});
if(t.first==1) top^=1;
a.push_back({t.second, -1});
if(t.second==2*n) bottom^=1;
}
a.push_back({1, 0});
a.push_back({2*n, 0});
sort(a.begin(), a.end());
bool sum=0;
// cerr<<"p="<<p<<endl;
for(int l=0, r; l<(int)a.size(); l=r+1){
for(r=l; r+1<(int)a.size() && a[r+1].first==a[l].first; ++r);
for(int i=l; i<=r; ++i) sum+=a[i].second;
if(a[l].first==2*n) break;
// cerr<<" "<<a[l].first<<" "<<a[r+1].first-1<<" sum="<<sum<<endl;
if(sum%2) modify(1, 1, 2*n-1, a[l].first, a[r+1].first-1);
else ans+=query(1, 1, 2*n-1, a[l].first, a[r+1].first-1).sum;
}
ans+=!top && !tree[1].l;
ans+=!bottom && !tree[1].r;
}
printf("%lld\n", ans);
return 0;
}
静渊以有谋,疏通而知事。