题解 P4898【[IOI2018] seats 排座位】
前言
这道题并不是我自己思考出来的,而是借助了其余题解的帮助。
写这个题解主要为了加深理解,想不看题解自己写一遍出来,确认自己已经懂了这道题。您可以将这篇题解看成我的做题记录。
尽可能地写得清晰一些,哪部分看不懂可以留言提醒。
$\text{Update 2022.6.29}$:修正一处笔误。
题意
有一个 $n\times m$ 的点构成的 $n\times m$ 的矩阵,设 $S(i)$ 表示编号为 $1$ 至 $i$ 的点是否构成矩形,$q$ 次操作,每次交换两个编号的点的位置,然后求出 $|\{i|S(i)\}|$。
$1\le n\times m\le 10^6,1\leq q\le5\times 10^4$.
思路
$\text{Section 1}$ 转化
首先我们思考怎么对于一个 $i$ 求出 $S(i)$。
与其他题解一样,我们将前 $i$ 个点染为黑色,其余染成白色。
我们考虑使用染出来的颜色设计出 $S(i)$ 为真的充要条件。
成为矩形的充要条件有:
- 点处于一个连通块内
- 图形不存在“$\text{L}$ 型”。
对于条件 $1$,我们有:
- 对于所有黑点,其左上方两点中均不为黑色的点数为 $1$。
对于条件 $2$,我们有:
- 对于所有白点,与其四连通的点内有不少于两个黑点的个数为 $0$。
于是我们可以依次计算。
$\text{Section 2}$ 优化
我们设点 $(i,j)$ 的编号为 $num_{(i,j)}$,编号为 $i$ 的点为 $(x_i,y_i)$。
考虑对于 $\text{Section 1}$ 的算法将 $i$ 由 $1$ 枚举到 $n\times m$,则 $(i,j)$ 被染为黑色的时间点为 $num_{(i,j)}$。
我们考虑对于点 $(a,b)$,其满足条件 $1$、$2$ 的时间段。
设 $i$ 为当前时间点。
此处我们不考虑边缘情况。
若满足条件 $1$,则:
- $(a,b)$ 是黑点,即 $num_{(a,b)}\le i$;
- $(a,b)$ 左方是白点,即 $num_{(a-1,b)}>i$ ;
- $(a,b)$ 上方是白点,即 $num_{(a,b-1)}>i$。
对这些条件取并集,即 $i\in[num_{(a,b)},\min(num_{(a-1,b)},num_{(a,b-1)}))$。
若满足条件 $2$,则:
- $(a,b)$ 是白点,即 $num_{(a,b)}>i$;
- $(a,b)$ 四周 $num$ 次小的值所在的点为黑点,即 $\text{secmin}(num_{(a-1,b)},num_{(a,b-1)},num_{(a+1,b)},num_{(a,b+1)})\le i$。
对这些条件取并集,即 $i\in[\text{secmin}(num_{(a-1,b)},num_{(a,b-1)},num_{(a+1,b)},num_{(a,b+1)}),num_{(a,b)})$。
此处需要搞清楚点被染黑点的条件、点被染成黑色的时间段等概念,不能混淆。
$\text{Section 3}$ 数据结构
我们需要一种数据结构,支持上面的操作。
对于每个点我们维护 $[$ 满足条件 $1\,]+[$ 满足条件 $2\,]$,那么我们对于每个合法区间进行 $+1$,查询全局有几个 $1$。
我们可以将满足条件一的点数设为 $x$,满足条件二的点数设为 $y$。
显然 $x\ge 1,y\ge0$,即 $x+y\ge 1$。
所以维护区间最小值及出现次数即答案.
$\text{Section 4}$ 实现细节
-
1.线段树初始最小值为 $0$ 情况的处理:
我这里也采用了维护严格次小值及其出现次数的方法,查询判断最小值是否为 $1$,严格次小值是否为 $1$ 即可。
-
2.修改点集出现重叠情况时需要去重。
-
3.我们将二维的 $num$ 数组重编号为 $1$ 至 $n\times m$ 维护较为方便。
-
4.考虑边界问题,设计的重编号函数检测到越界时可以返回 $n\times m+1$,此时 $num_{n\times m+1}=n\times m+1$,线段树区间修改时判断当修改区间左端点大于右端点是返回即可。
最终时间复杂度为 $O((nm+q)\log nm)$,常数巨大就对了,带个线段树的 $4$ 和一次修改的 $2$、修改点数的 $10$。
代码(挺容易写):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define mpr make_pair
namespace IO{//by cyffff
}
const int N=1e6+10,INF=1e7;
int num[N];
pii pos[N];
int n,m,su;
int mx[]={0,1,0,-1,0};
int my[]={1,0,-1,0,0};
inline int number(int x,int y){
if(min(x,y)<1||x>n||y>m) return su;
return (x-1)*m+y;
}
struct Segument_Tree{
struct node{
int l,r,minn,tag,cnt,secm=INF,sec;
inline node operator+=(const int &x){
minn+=x;
secm+=x;
tag+=x;
return *this;
}
}a[N<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
inline void getsec(int &x,int &y,int &sx,int &sy,int a,int b){
if(x>a){
sx=x,sy=y,x=a,y=b;
}else if(x==a){
y+=b;
}else if(sx>a){
sx=a,sy=b;
}else if(sx==a){
sy+=b;
}
}
inline void pushup(int rt){
a[rt].cnt=a[rt].sec=0;
a[rt].minn=a[rt].secm=INF;
getsec(a[rt].minn,a[rt].cnt,a[rt].secm,a[rt].sec,a[ls].minn,a[ls].cnt);
getsec(a[rt].minn,a[rt].cnt,a[rt].secm,a[rt].sec,a[ls].secm,a[ls].sec);
getsec(a[rt].minn,a[rt].cnt,a[rt].secm,a[rt].sec,a[rs].minn,a[rs].cnt);
getsec(a[rt].minn,a[rt].cnt,a[rt].secm,a[rt].sec,a[rs].secm,a[rs].sec);
}
inline void pushdown(int rt){
if(!a[rt].tag) return ;
a[ls]+=a[rt].tag;
a[rs]+=a[rt].tag;
a[rt].tag=0;
}
inline void build(int rt,int l,int r){
a[rt].l=l,a[rt].r=r;
a[rt].cnt=r-l+1;
if(l==r) return ;
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
inline void update(int rt,int l,int r,int v){
if(l>r) return ;
if(a[rt].l>=l&&r>=a[rt].r){
a[rt]+=v;
return ;
}
pushdown(rt);
if(a[ls].r>=l) update(ls,l,r,v);
if(a[rs].l<=r) update(rs,l,r,v);
pushup(rt);
}
inline int query(){
return (a[1].minn==1)*a[1].cnt+(a[1].secm==1)*a[1].sec;
}
}t;
int tind[10];
inline void update(int x,int y,int v){
int posi=number(x,y);
if(posi==su) return ;
t.update(1,num[posi],min(num[number(x-1,y)],num[number(x,y-1)])-1,v);
for(int i=0;i<4;i++){
tind[i]=num[number(x+mx[i],y+my[i])];
}
sort(tind,tind+4);
t.update(1,tind[1],num[posi]-1,v);
}
pii tmp[15],tmp1,tmp2;
int q,top;
int main(){
n=read(),m=read(),q=read();
num[su=n*m+1]=n*m+1;
for(int i=1;i<su;i++){
int x=read()+1,y=read()+1;
num[number(x,y)]=i;
pos[i]=mpr(x,y);
}
t.build(1,1,su-1);
for(int i=1;i<su;i++){
update(pos[i].first,pos[i].second,1);
}
while(q--){
int x=read()+1,y=read()+1;
tmp1=pos[x],tmp2=pos[y];
top=0;
for(int i=0;i<5;i++){
tmp[++top]=mpr(tmp1.first+mx[i],tmp1.second+my[i]);
tmp[++top]=mpr(tmp2.first+mx[i],tmp2.second+my[i]);
}
sort(tmp+1,tmp+top+1);
top=unique(tmp+1,tmp+top+1)-tmp;
for(int i=1;i<top;i++){
update(tmp[i].first,tmp[i].second,-1);
}
swap(num[number(tmp1.first,tmp1.second)],num[number(tmp2.first,tmp2.second)]);
swap(pos[x],pos[y]);
for(int i=1;i<top;i++){
update(tmp[i].first,tmp[i].second,1);
}
write(t.query());
putc('\n');
}
flush();
return 0;
}
再见 qwq~

浙公网安备 33010602011771号