LOJ #3272. 「JOISC 2020 Day1」汉堡肉

题目链接

LOJ #3272. 「JOISC 2020 Day1」汉堡肉

题目大意

平面上分布着 \(n\) 个矩形,矩形之间可重叠,给定 \(K\),要求找出 \(K\)\(1\times 1\) 的格子,满足对于任意一个矩形都存在一个格子在其内部。输出方案,数据保证有解。

\(1\leq n\leq 2\times 10^5,\;1\leq K\leq 4\),坐标值域 \(10^9\) 且均为整数。

思路

找到所有矩形中最靠左的右边界 \(rb\),因为需要存在选定的点横坐标 \(\leq rb\),而 \(rb\) 左侧没有别的需要另外覆盖的地方,所以最优方案中必然存在一个点横坐标为 \(rb\),且 \(rb\) 左侧无点。

剩下几个方向类似,容易发现 \(K\) 的分布区域为四个最值边界组成的矩形,并且矩形每条边上都至少要有一个点。

\(K\leq 3\) 的时候,必然存在一个点在矩形的一个角上,枚举这个点所在的角,然后删去所有包含该点的矩形,此时变成子问题,递归求解即可,是 \(O(4^Kn)\) 的。

\(K=4\) 时,若有点在角上的情况,按上处理即可,剩下还有一种情况,就是 \(4\) 个点都在矩形的边上。现在我们分析原来 \(n\) 个矩形和这个大矩形的位置关系,由于保证有解,所以两者必然相交:

  • 完整包含大矩形或触及了其中三条边:此时必然有一条边在当前矩形内部,必然有解。
  • 仅和一条边有交:则位于这条边上的点必然处在这个相交的区间内。
  • 和两条边有交:如从中间贯穿大矩形或触及一角,这是二者满足一个的情况,要么其中一条边上的点在相交区间,要么另外一边满足。

容易发现和两条边相交的情况是一个 \(2-SAT\) 问题,以限制点的区间为决策,一条边上两区间相离即矛盾的情况。每个方向上区间排序后建立前缀与后缀索引,便可以 \(O(n\log n)\) 建图,最后 \(SCC\) 缩点后逆拓扑序构造方案,在限制区间的交集中任取一点即可。

时间复杂度 \(O(n(4^K+\log n))\)

Code

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<stack>
#include<fstream>
#include<tuple>
#define mem(a,b) memset(a, b, sizeof(a))
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define per(i,b,a) for(int i = (b); i >= (a); i--)
#define N 200022
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define fr first
#define sc second
#define all(p) p.begin(), p.end()
using namespace std;

int n, k;
int lb = 1, rb = Inf, db = 1, ub = Inf;
struct square{ int l, r, d, u; } p[N];

namespace corners{
    bool rem[N];
    vector<PII> sol;

    bool dfs(){
        if(sol.size() >= k){
            bool ok = true;
            rep(i,1,n) ok &= !rem[i];
            return ok;
        }
        int lb = 1, rb = Inf, db = 1, ub = Inf;
        rep(i,1,n) if(rem[i])
            lb = max(lb, p[i].l), rb = min(rb, p[i].r),
            db = max(db, p[i].d), ub = min(ub, p[i].u);
        for(PII x : vector<PII> {{lb, db}, {lb, ub}, {rb, db}, {rb, ub}}){
            vector<int> select;
            rep(i,1,n) if(rem[i])
                if(x.fr >= p[i].l && x.fr <= p[i].r && x.sc >= p[i].d && x.sc <= p[i].u)
                    rem[i] = false, select.push_back(i);
            sol.push_back(x);
            if(dfs()) return true;
            sol.pop_back();
            for(int i : select) rem[i] = true;
        }
        return false;
    }

    bool solve(){
        mem(rem, true);
        if(!dfs()) return false;
        for(PII x : sol) cout<< x.fr <<" " << x.sc <<endl;
        return true;
    }
};

namespace edges{
    int head[8*N], nxt[8*N], to[8*N];
    int cnt, num, dfsid, scc;
    int dfn[8*N], low[8*N], c[8*N];
    int lbn, lbx, rbn, rbx, ubn, ubx, dbn, dbx;
    bool in[8*N];
    stack<int> stk;

    vector<int> dir[N];
    struct data{
        int d1, d2, l1, r1, l2, r2;
    }; vector<data> D;
    struct pair_seg{
        int l, r, id, res;
        int exl, exr;
    }; vector<pair_seg> vec[4];

    void init(){ mem(head, -1), cnt = -1; }
    void add_e(int a, int b){
        nxt[++cnt] = head[a], head[a] = cnt, to[cnt] = b;
    }

    bool Min(int &a, int b){ return a > b ? a = b, 1 : 0; }
    bool Max(int &a, int b){ return a < b ? a = b, 1 : 0; }

    void get_segments(){
        rep(i,1,n){
            if(lb >= p[i].l && lb <= p[i].r) dir[i].push_back(0);
            if(rb >= p[i].l && rb <= p[i].r) dir[i].push_back(1);
            if(db >= p[i].d && db <= p[i].u) dir[i].push_back(2);
            if(ub >= p[i].d && ub <= p[i].u) dir[i].push_back(3);
        }
        lbn = rbn = db, lbx = rbx = ub, dbn = ubn = lb, dbx = ubx = rb;
        rep(i,1,n) if(dir[i].size() == 1){
            switch(dir[i][0]){
                case 0: Max(lbn, p[i].d), Min(lbx, p[i].u); break;
                case 1: Max(rbn, p[i].d), Min(rbx, p[i].u); break;
                case 2: Max(dbn, p[i].l), Min(dbx, p[i].r); break;
                case 3: Max(ubn, p[i].l), Min(ubx, p[i].r);
            }
        }
        rep(i,1,n) if(dir[i].size() == 2){
            D.push_back({}); data &cur = D.back();
            rep(j,0,1){
                int d = dir[i][j];
                int l = (d <= 1 ? p[i].d : p[i].l), r = (d <= 1 ? p[i].u : p[i].r);
                Max(l, d == 0 ? lbn : (d == 1 ? rbn : (d == 2 ? dbn : ubn)));
                Min(r, d == 0 ? lbx : (d == 1 ? rbx : (d == 2 ? dbx : ubx)));
                j ? (cur.d2 = d, cur.l2 = l, cur.r2 = r) : (cur.d1 = d, cur.l1 = l, cur.r1 = r);
            }
        }
    }

    void build(){
        init();
        for(data p : D){
            if(p.l1 <= p.l1) vec[p.d1].push_back({p.l1, p.r1, num+1, num+2});
            else add_e(num+1, num+2);
            if(p.l2 <= p.r2) vec[p.d2].push_back({p.l2, p.r2, num+2, num+1});
            else add_e(num+2, num+1);
            num += 2;
        }
        auto cmpl = [&](pair_seg a, pair_seg b){ return PII{a.l, a.r} < PII{b.l, b.r}; };
        auto cmpr = [&](pair_seg a, pair_seg b){ return PII{a.r, a.l} < PII{b.r, b.l}; };
        rep(d,0,3){
            sort(all(vec[d]), cmpl);
            int lst = 0;
            for(pair_seg &a : vec[d]){
                a.exl = ++num, add_e(a.exl, a.res);
                if(lst) add_e(lst, a.exl); lst = a.exl;
            }
            for(pair_seg a : vec[d]){
                auto it = upper_bound(all(vec[d]), pair_seg{a.r, Inf}, cmpl);
                if(it != vec[d].end()) add_e(a.id, it->exl);
            }
            sort(all(vec[d]), cmpr);
            lst = 0;
            for(pair_seg &a : vec[d]){
                a.exr = ++num, add_e(a.exr, a.res);
                if(lst) add_e(a.exr, lst); lst = a.exr;
                auto it = lower_bound(all(vec[d]), pair_seg{0, a.l}, cmpr);
                if(it != vec[d].begin()) add_e(a.id, (--it)->exr);
            }
        }
    }

    void tarjan(int x){
        low[x] = dfn[x] = ++dfsid;
        stk.push(x), in[x] = true;
        for(int i = head[x]; ~i; i = nxt[i]){
            int y = to[i];
            if(!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
            else if(in[y]) low[x] = min(low[x], dfn[y]);
        }
        if(dfn[x] == low[x]){
            int y; scc++;
            do{
                y = stk.top(); stk.pop();
                in[y] = false, c[y] = scc;
            } while(y != x);
        }
    }

    void solve(){
        get_segments(), build();
        rep(i,1,n) if(dir[i].size() == 0){ cout<<"No sol\n"; return; }
        rep(i,1,num) if(!dfn[i]) tarjan(i);
        
        vector<PII> ans;
        rep(d,0,3){
            int l, r;
            tie(l, r) = d == 0 ? tie(lbn, lbx) : (d == 1 ? tie(rbn, rbx) : d == 2 ? tie(dbn, dbx) : tie(ubn, ubx));
            for(pair_seg a : vec[d]){
                if(c[a.id] == c[a.res]){ cout<<"I want my Wrong Answer!\n"; return; }
                if(c[a.id] < c[a.res]) l = max(l, a.l), r = min(r, a.r);
            }
            int bound = d == 0 ? lb : (d == 1 ? rb : (d == 2 ? db : ub));
            ans.push_back(d <= 1 ? PII{bound, l} : PII{l, bound});
        }
        for(PII p : ans) cout<< p.fr <<" "<< p.sc <<endl;
    }
};

int main(){
    ios::sync_with_stdio(false);
    cin>>n>>k;
    rep(i,1,n){
        cin>> p[i].l >> p[i].d >> p[i].r >> p[i].u;
        lb = max(lb, p[i].l), rb = min(rb, p[i].r);
        db = max(db, p[i].d), ub = min(ub, p[i].u);
    }
    if(lb > rb) swap(lb, rb);
    if(db > ub) swap(db, ub);

    if(k <= 3) corners::solve();
    else if(!corners::solve()) edges::solve();
    return 0;
}
posted @ 2022-01-23 21:18  Neal_lee  阅读(138)  评论(0编辑  收藏  举报