The 2021 ICPC Asia Taipei Regional F. What a Colorful Wall 扫描线 + 并查集

文章目录

传送门

题意:

给你平面 n n n个矩形,每个矩形有一种颜色,依次给出矩形以及其的颜色,后面的矩形会覆盖前面的矩形,问最终有多少种颜色。

1 ≤ n ≤ 4000 , 0 ≤ x 1 < x 2 < 2 28 , 0 ≤ y 1 < y 2 < 2 28 , 1 ≤ c ≤ n 1\le n\le 4000,0\le x_1<x_2< 2^{28},0\le y_1<y_2< 2^{28},1\le c\le n 1n4000,0x1<x2<228,0y1<y2<228,1cn

思路

首先第一个应该想到的就是倒着来,因为后面的会覆盖前面的。

其次由于坐标范围很大,不难想到离散化一下。

矩阵的问题,很容易往扫描线上靠,让后看一下 n n n,显然可以想到 n 2 l o g n n^2logn n2logn的算法。

矩形扫描线的第一步也是最重要的一步,就是将线段化点,因为矩形如果只存点的话,是显然不对的,比如第三个样例,如果只考虑点的话就会漏掉一种颜色。

让后就是暴力的考虑对于每个 x x x,我们倒着将 n n n个矩形插入,用并查集维护已经有颜色的集合,让后遍历,合并即可。

复杂度 O ( n 2 α ) O(n^2\alpha) O(n2α)

#include<bits/stdc++.h>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
using namespace std;

const int N=1000010,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;

int n;
vector<int>ax,ay;
struct Mat {
    int x1,y1,x2,y2,c;
}p[N];
struct DSU {
    vector<int>p,se;
    DSU(int n) : p(n), se(n, 1) { std::iota(p.begin(), p.end(), 0); }
    int find(int x) {
        return x==p[x]? x:p[x]=find(p[x]);
    }
    bool merge(int x,int y) {
        int px=find(x),py=find(y);
        if(px==py) return false;
        p[px]=py;
        se[py]+=se[px];
        return true;
    }
    int size(int u) {
        return se[find(u)];
    }
};

int find(vector<int>v,int x) {
    return lower_bound(v.begin(),v.end(),x)-v.begin();
}

void solve() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        int x1,y1,x2,y2,c;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
        p[i]={x1,y2,x2,y1,c};
        ax.push_back(x1);
        ax.push_back(x2);
        ay.push_back(y1);
        ay.push_back(y2);
    }
    sort(ax.begin(),ax.end()); ax.erase(unique(ax.begin(),ax.end()),ax.end());
    sort(ay.begin(),ay.end()); ay.erase(unique(ay.begin(),ay.end()),ay.end());
    for(int i=1;i<=n;i++) {
        int x1=find(ax,p[i].x1),y1=find(ay,p[i].y1);
        int x2=find(ax,p[i].x2),y2=find(ay,p[i].y2);
        p[i]={x1,y1,x2,y2,p[i].c};
    }
    vector<int>st(n+1,0);
    for(int i=0;i<ax.size();i++) {
        DSU dsu(ax.size()*2+1);
        for(int j=n;j>=1;j--) {
            int x1=p[j].x1,y1=p[j].y1;
            int x2=p[j].x2,y2=p[j].y2;
            if(x1<=i&&i<x2) {
                for(int k=dsu.find(y1);k<y2;k=dsu.find(k)) {
                    dsu.merge(k,k+1);
                    st[p[j].c]=1;
                }
            }
        }
    }
    cout<<count(st.begin(),st.end(),1)<<endl;
}

int main() {
	int _=1;
	while(_--) {
		solve();
	}
    
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值