luogu P9261 [PA 2022] Płótno / QOJ5242 Płótno [B] 题解

link1 link2

考虑如何快速计算联通块数量。对于一个森林,联通块数量就为点数减去边数,但是有环就会少算,我们还需要加上 \(2\times 2\) 方格的数量。

考虑离线扫描线,右端点从左往右遍历,我们用线段树维护前 \(k+1\) 大值和个数。新加入一个点时,先把所有包含这个点的区间加 \(1\),再把包含它与直接相邻的点连成的边区间加 \(1\),然后把包含它新增的 \(2\times 2\) 方格的区间减 \(1\)

需要注意的是,因为它是一个环,所以如果出现一个联通块同时达到左边界和右边界,联通块数量会被判为 \(0\),所以我们把 \(0\) 的答案加到 \(1\) 里面。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lc(i) ((i)<<1)
#define rc(i) ((i)<<1|1)
const int N=5e5+5,M=15;
const int inf=1e9;
int n,k,a[2][N];ll ans[M];pair<int,int> pos[N];
pair<int,ll> mn[N<<2][M];ll add[N<<2];
void pushup(int i){
    for(int j=1,x=1,y=1;j<=k+1;j++){
        if(mn[lc(i)][x].first<mn[rc(i)][y].first){
            mn[i][j]=mn[lc(i)][x];
            x++;
        }else if(mn[lc(i)][x].first==mn[rc(i)][y].first){
            mn[i][j]={mn[lc(i)][x].first,mn[lc(i)][x].second+mn[rc(i)][y].second};
            x++;y++;
        }else{
            mn[i][j]=mn[rc(i)][y];
            y++;
        }
    }
}
void apply_add(int i,ll v){
    for(int j=1;j<=k+1;j++)mn[i][j].first+=v;
    add[i]+=v;
}
void pushdown(int i){
    if(add[i]){
        apply_add(lc(i),add[i]);
        apply_add(rc(i),add[i]);
        add[i]=0;
    }
}
void build(int i,int tl,int tr){
    if(tl==tr){
        mn[i][1]={0,1};
        for(int j=2;j<=k+1;j++)mn[i][j]={inf,0};
        return;
    }
    int tm=(tl+tr)>>1;
    build(lc(i),tl,tm);
    build(rc(i),tm+1,tr);
    pushup(i);
}
void update(int i,int tl,int tr,int l,int r,ll v){
    if(l<=tl&&tr<=r){
        apply_add(i,v);
        return;
    }
    pushdown(i);
    int tm=(tl+tr)>>1;
    if(l<=tm)update(lc(i),tl,tm,l,r,v);
    if(r>tm)update(rc(i),tm+1,tr,l,r,v);
    pushup(i);
}
int main(){
    // freopen("canvas.in","r",stdin);
    // freopen("canvas.out","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>k;
    for(int i=0;i<=1;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
            pos[a[i][j]]={i,j};
        }
    }
    build(1,1,2*n);
    for(int i=1;i<=2*n;i++){
        int x=pos[i].first,y=pos[i].second;
        int l=a[x][y-1+(y==1?n:0)],r=a[x][y+1-(y==n?n:0)],L=a[x^1][y-1+(y==1?n:0)],R=a[x^1][y+1-(y==n?n:0)],u=a[x^1][y];
        update(1,1,2*n,1,i,1);
        if(max(l,max(L,u))<i)update(1,1,2*n,1,min(l,min(L,u)),1);
        if(max(r,max(R,u))<i)update(1,1,2*n,1,min(r,min(R,u)),1);
        if(l<i)update(1,1,2*n,1,l,-1);
        if(r<i)update(1,1,2*n,1,r,-1);
        if(u<i)update(1,1,2*n,1,u,-1);
        for(int j=1;j<=k+1;j++){
            if(mn[1][j].first>k)break;
            if(mn[1][j].first==0)ans[1]+=mn[1][j].second-2*n+i;//后面的左端点还没有算到,这些区间不存在,所以需要减去
            ans[mn[1][j].first]+=mn[1][j].second;
        }
    }
    for(int i=1;i<=k;i++)cout<<ans[i]<<" ";
    return 0;
}
posted @ 2026-09-03 08:34  cqbzcjh  阅读(3)  评论(0)    收藏  举报