题解 P4898【[IOI2018] seats 排座位】

Link

前言

这道题并不是我自己思考出来的,而是借助了其余题解的帮助。

写这个题解主要为了加深理解,想不看题解自己写一遍出来,确认自己已经懂了这道题。您可以将这篇题解看成我的做题记录。

尽可能地写得清晰一些,哪部分看不懂可以留言提醒。

Update 2022.6.29:修正一处笔误。

题意

有一个 n×m 的点构成的 n×m 的矩阵,设 S(i) 表示编号为 1i 的点是否构成矩形,q 次操作,每次交换两个编号的点的位置,然后求出 |{i|S(i)}|

1n×m106,1q5×104.

思路

Section 1 转化

首先我们思考怎么对于一个 i 求出 S(i)

与其他题解一样,我们将前 i 个点染为黑色,其余染成白色。

我们考虑使用染出来的颜色设计出 S(i) 为真的充要条件。

成为矩形的充要条件有:

  • 点处于一个连通块内
  • 图形不存在“L 型”。

对于条件 1,我们有:

  • 对于所有黑点,其左上方两点中均不为黑色的点数为 1

对于条件 2,我们有:

  • 对于所有白点,与其四连通的点内有不少于两个黑点的个数为 0

于是我们可以依次计算。

Section 2 优化

我们设点 (i,j) 的编号为 num(i,j),编号为 i 的点为 (xi,yi)

考虑对于 Section 1 的算法将 i1 枚举到 n×m,则 (i,j) 被染为黑色的时间点为 num(i,j)

我们考虑对于点 (a,b),其满足条件 12 的时间段。

i 为当前时间点。

此处我们不考虑边缘情况。

若满足条件 1,则:

  • (a,b) 是黑点,即 num(a,b)i
  • (a,b) 左方是白点,即 num(a1,b)>i ;
  • (a,b) 上方是白点,即 num(a,b1)>i

对这些条件取并集,即 i[num(a,b),min(num(a1,b),num(a,b1)))

若满足条件 2,则:

  • (a,b) 是白点,即 num(a,b)>i
  • (a,b) 四周 num 次小的值所在的点为黑点,即 secmin(num(a1,b),num(a,b1),num(a+1,b),num(a,b+1))i

对这些条件取并集,即 i[secmin(num(a1,b),num(a,b1),num(a+1,b),num(a,b+1)),num(a,b))

此处需要搞清楚点被染黑点的条件、点被染成黑色的时间段等概念,不能混淆。

Section 3 数据结构

我们需要一种数据结构,支持上面的操作。

对于每个点我们维护 [ 满足条件 1]+[ 满足条件 2],那么我们对于每个合法区间进行 +1,查询全局有几个 1

我们可以将满足条件一的点数设为 x,满足条件二的点数设为 y

显然 x1,y0,即 x+y1

所以维护区间最小值及出现次数即答案.

Section 4 实现细节

  • 1.线段树初始最小值为 0 情况的处理:

    我这里也采用了维护严格次小值及其出现次数的方法,查询判断最小值是否为 1,严格次小值是否为 1 即可。

  • 2.修改点集出现重叠情况时需要去重。

  • 3.我们将二维的 num 数组重编号为 1n×m 维护较为方便。

  • 4.考虑边界问题,设计的重编号函数检测到越界时可以返回 n×m+1,此时 numn×m+1=n×m+1,线段树区间修改时判断当修改区间左端点大于右端点是返回即可。


最终时间复杂度为 O((nm+q)lognm),常数巨大就对了,带个线段树的 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~

posted @   ffffyc  阅读(15)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示