题解 P4898【[IOI2018] seats 排座位】
前言
这道题并不是我自己思考出来的,而是借助了其余题解的帮助。
写这个题解主要为了加深理解,想不看题解自己写一遍出来,确认自己已经懂了这道题。您可以将这篇题解看成我的做题记录。
尽可能地写得清晰一些,哪部分看不懂可以留言提醒。
:修正一处笔误。
题意
有一个 的点构成的 的矩阵,设 表示编号为 至 的点是否构成矩形, 次操作,每次交换两个编号的点的位置,然后求出 。
.
思路
转化
首先我们思考怎么对于一个 求出 。
与其他题解一样,我们将前 个点染为黑色,其余染成白色。
我们考虑使用染出来的颜色设计出 为真的充要条件。
成为矩形的充要条件有:
- 点处于一个连通块内
- 图形不存在“ 型”。
对于条件 ,我们有:
- 对于所有黑点,其左上方两点中均不为黑色的点数为 。
对于条件 ,我们有:
- 对于所有白点,与其四连通的点内有不少于两个黑点的个数为 。
于是我们可以依次计算。
优化
我们设点 的编号为 ,编号为 的点为 。
考虑对于 的算法将 由 枚举到 ,则 被染为黑色的时间点为 。
我们考虑对于点 ,其满足条件 、 的时间段。
设 为当前时间点。
此处我们不考虑边缘情况。
若满足条件 ,则:
- 是黑点,即 ;
- 左方是白点,即 ;
- 上方是白点,即 。
对这些条件取并集,即 。
若满足条件 ,则:
- 是白点,即 ;
- 四周 次小的值所在的点为黑点,即 。
对这些条件取并集,即 。
此处需要搞清楚点被染黑点的条件、点被染成黑色的时间段等概念,不能混淆。
数据结构
我们需要一种数据结构,支持上面的操作。
对于每个点我们维护 满足条件 满足条件 ,那么我们对于每个合法区间进行 ,查询全局有几个 。
我们可以将满足条件一的点数设为 ,满足条件二的点数设为 。
显然 ,即 。
所以维护区间最小值及出现次数即答案.
实现细节
-
1.线段树初始最小值为 情况的处理:
我这里也采用了维护严格次小值及其出现次数的方法,查询判断最小值是否为 ,严格次小值是否为 即可。
-
2.修改点集出现重叠情况时需要去重。
-
3.我们将二维的 数组重编号为 至 维护较为方便。
-
4.考虑边界问题,设计的重编号函数检测到越界时可以返回 ,此时 ,线段树区间修改时判断当修改区间左端点大于右端点是返回即可。
最终时间复杂度为 ,常数巨大就对了,带个线段树的 和一次修改的 、修改点数的 。
代码(挺容易写):
#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~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具