【学习笔记】「JOISC 2020 Day1」汉堡肉

solution:

考点:数学 + 2-sat 模型。

观察 k ≤ 4 k\leq 4 k4 肯定有猫腻。

设所有正方形的右边界最小值为 m n x mnx mnx

则可以证明若有解一定存在一个竹签使得其纵坐标为 m n x mnx mnx ,即调整坐标得到不劣的方案。

类似的也有同样的性质。对于 k ≤ 3 k\leq 3 k3 的情况,根据鸽巢原理,一定存在一个竹签落在四个角上。直接 O ( 4 k n ) O(4^kn) O(4kn) 爆搜即可。

对于 k = 4 k=4 k=4 的情况,正确考虑方式为:如果一个矩形如果和三条直线相交,那么一定包含一条边界,可以不用考虑;否则考虑 2-sat 模型。限制条件为:若两个区间不存在交,则一定不能被同一条边覆盖。

那我们可以把 l, r 端点分别 push_back 进每一个可能满足的边,对于每个 l, 向满足 r<l 的点建边,同理,对于每个 r,向满足 l>r 的点建边。可以 two-pointers 完成。这样要构造 2*3=6 倍节点。

时间复杂度 O ( n ( l o g n + 4 k ) ) O(n(logn+4^k)) O(n(logn+4k)) 。代码好恶心。

代码是 z y y zyy zyy 的,做了一些改动。 d e b u g debug debug 了一晚上终于改对了代码。

#include <bits/stdc++.h>
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define ll long long
#define fi first
#define se second
#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);i++)
#define Rep(i,j,k) for (int i=(int)(j);i>=(int)(k);i--)
using namespace std;
const int mx = 2e5 + 5;
const int mxn = mx * 6;
int n, k, nd, cnt[mx], id[mx][2], pr[mx], su[mx], at[mx][2];
int dfn[mxn], low[mxn], vis[mxn], c[mxn], s[mxn], tp, bl, num;
struct qry {
    int x, v0, v1;
    bool operator <(const qry &a) const {
        return x < a.x;
    }
};
vector<qry> vl[4], vr[4];
vector<int> g[mxn];
PII ans[5];
struct node {
    int x, y, x2, y2;
} a[mx];
void add(int x, int y) {
    if (!x || !y)
        return;

    g[x].push_back(y);
}
void change(int x, int y, int r) {
    for (int i = 1; i <= n; i++) {
        if (a[i].x <= x && x <= a[i].x2 && a[i].y <= y && y <= a[i].y2) {
            cnt[i] += r;
        }
    }
}
void dfs(int d) {
    int mnx = 1e9, mxx = 1;
    int mny = 1e9, mxy = 1;

    for (int i = 1; i <= n; i++) {
        if (!cnt[i]) {
            mnx = min(mnx, a[i].x2);
            mxx = max(mxx, a[i].x);
            mny = min(mny, a[i].y2);
            mxy = max(mxy, a[i].y);
        }
    }

    if (d == 1) {
        if (mxx <= mnx && mxy <= mny) {
            printf("%d %d\n", mnx, mny);

            for (int i = 2; i <= k; i++) {
                printf("%d %d\n", ans[i].fi, ans[i].se);
            }

            exit(0);
        }

        return;
    }

    int a[2] = {mnx, mxx}, b[2] = {mny, mxy};

    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            ans[d] = make_pair(a[i], b[j]);
            change(a[i], b[j], 1), dfs(d - 1), change(a[i], b[j], -1);
        }
    }
}
void tarjan(int x) {
    dfn[x] = low[x] = ++num, vis[x] = 1, s[++tp] = x;

    for (auto y : g[x]) {
        if (!dfn[y])
            tarjan(y), low[x] = min(low[x], low[y]);
        else if (vis[y])
            low[x] = min(low[x], dfn[y]);
    }

    if (low[x] == dfn[x]) {
        int tmp(0);
        bl++;

        do {
            tmp = s[tp--];
            c[tmp] = bl;
            vis[tmp] = 0;
        } while (tmp != x);
    }
}
int main() {
    //  freopen("test.in","r",stdin);
    //  freopen("own.out","w",stdout);
    scanf("%d%d", &n, &k);

    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d%d", &a[i].x, &a[i].y, &a[i].x2, &a[i].y2);
    }

    dfs(k);
    int mnx = 1e9, mxx = 1;
    int mny = 1e9, mxy = 1;
    For(i, 1, n) {
        mnx = min(mnx, a[i].x2);
        mxx = max(mxx, a[i].x);
        mny = min(mny, a[i].y2);
        mxy = max(mxy, a[i].y);
    }

    if (mnx > mxx)
        swap(mnx, mxx);

    if (mny > mxy)
        swap(mny, mxy);

    For(i, 1, n) {
        a[i].x = max(a[i].x, mnx);
        a[i].x2 = min(a[i].x2, mxx);
        a[i].y = max(a[i].y, mny);
        a[i].y2 = min(a[i].y2, mxy);
    }

    for (int i = 1; i <= n; i++) {
        int fl = 0, s = 0, d = 0;

        if (a[i].x <= mnx && mnx <= a[i].x2)
            fl |= 1, s++;

        if (a[i].x <= mxx && mxx <= a[i].x2)
            fl |= 2, s++;

        if (a[i].y <= mny && mny <= a[i].y2)
            fl |= 4, s++;

        if (a[i].y <= mxy && mxy <= a[i].y2)
            fl |= 8, s++;

        if (s >= 3)
            continue;

        if (s == 0)
            return 0;

        id[i][0] = ++nd;
        id[i][1] = ++nd;

        if (s == 1)
            g[nd].push_back(nd - 1);

        for (int j = 0; j <= 3; j++) {
            if (fl >> j & 1) {
                at[i][d] = j;

                if (j & 2) {
                    vl[j].push_back({a[i].x, id[i][d], id[i][d ^ 1]});
                    vr[j].push_back({a[i].x2, id[i][d], id[i][d ^ 1]});
                    d++;
                } else {
                    vl[j].push_back({a[i].y, id[i][d], id[i][d ^ 1]});
                    vr[j].push_back({a[i].y2, id[i][d], id[i][d ^ 1]});
                    d++;
                }
            }
        }
    }

    for (int i = 0; i <= 3; i++) {
        if (!vl[i].size())
            continue;

        int sz = vl[i].size(), pos = 0;
        sort(vl[i].begin(), vl[i].end());
        sort(vr[i].begin(), vr[i].end());
        pr[0] = su[sz + 1] = 0;

        for (int j = 1; j <= sz; j++) {
            pr[j] = ++nd;
            add(pr[j], pr[j - 1]);
            add(pr[j], vr[i][j - 1].v1);
        }

        for (int j = sz; j >= 1; j--) {
            su[j] = ++nd;
            add(su[j], su[j + 1]);
            add(su[j], vl[i][j - 1].v1);
        }
        for (int j = 0; j < sz; j++) {
            for (; pos < sz && vl[i][j].x > vr[i][pos].x; pos++);

            add(vl[i][j].v0, pr[pos]);
        }
        pos = 0;
        for (int j = 0; j < sz; j++) {
            for (; pos < sz && vr[i][j].x >= vl[i][pos].x; pos++);

            add(vr[i][j].v0, su[pos + 1]);
        }

    }

    for (int i = 1; i <= nd; i++) {
        if (!dfn[i])
            tarjan(i);
    }

    for (int i = 1; i <= n; i++) {
        if (id[i][0] && c[id[i][0]] == c[id[i][1]]) {
            printf("-1");
            return 0;
        }
    }

    int mn[4], mx[4];
    For(i, 0, 3) {
        mn[i] = (i & 2 ? mnx : mny);
        mx[i] = (i & 2 ? mxx : mxy);
    }

    For(i, 1, n) if (id[i][0]) {
        int flg = (c[id[i][0]] > c[id[i][1]]);
        int idd = at[i][flg];
        mn[idd] = max(mn[idd], (idd & 2 ? a[i].x : a[i].y));
        mx[idd] = max(mx[idd], (idd & 2 ? a[i].x2 : a[i].y2));
    }

    printf("%d %d\n", mnx, mn[0]);
    printf("%d %d\n", mxx, mn[1]);
    printf("%d %d\n", mn[2], mny);
    printf("%d %d\n", mn[3], mxy);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值