【CF 793G】Oleg and Chess

这篇博客讨论了在有删除矩形限制的n×n棋盘上放置棋子的问题,要求每行每列至多一个棋子。通过分析,提出了使用线段树优化网络流建图的方法,并引入扫描线策略减少无效切割,将复杂度控制在可接受范围内。虽然代码实现较为复杂,但该策略提供了一种解决此类问题的思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

##题面
链接_洛谷
链接_CF
简述一下题意:
在一个 n ∗ n ( n ≤ 10000 ) n*n(n\leq10000) nn(n10000)的棋盘上放棋子,要求一行一列不能放两个棋子。现在删去 n n n个矩形,输入方式是给左下坐标和右上坐标。删去的矩形不能放棋子,但棋子隔着一个删去的矩形仍然会互相干扰。求最多放几个棋子。

##分析
首先考虑放棋子是什么意思(这是一个挺常见的转化),一行和一列去配对,每一行每一列只能配对一次。删去的部分就是这些行和这些列不可以配对。
连10000*10000条边显然要超时炸空间,那么我们考虑线段树优化建图。不了解优化建图的建议出门百度网络流优化建图。但是这个时候遇到一个问题,就是优化建图是一段区间向一段区间连边,但是我们只知道一段区间不能和一段区间连边。所以考虑如何把剩余部分划分成若干矩形。
一种十分容易想到的方式是直接从所有删掉的矩形的边那里切开(如图,红色是删掉的部分),然而如果删掉的是一条对角线上的所有矩形这就变暴力了TAT。
画图画的,轻喷qwq
我们观察一下这个切分方式:我们切分了很多无用的线,对于在它上面很多的矩形切下来的线,完全可以置之不理。
于是我们考虑扫描线。具体步骤如下:对于每一个矩形的下边,它以下部分切一个矩形出来。对于一个矩形的上边,左右两侧切两个矩形出来。如图。
这里写图片描述
现在来考察复杂度:对于每个矩形,遇到底边的时候出现一个矩形,顶边的时候出现两个矩形,于是我们一共会剖分 O ( n ) O(n) O(n)个矩形,可以接受。
细节有点麻烦,以后有功夫了上来写一下

代码

十分晦涩qwq
因为自己改了好多遍,拍出了无数错和没考虑好的地方所以就十分丑陋
以后也许会来补一补注释什么的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#include <ctime>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, S, T;
int q[MAXN<<6], level[MAXN<<3], Cnt[2], interval[2][MAXN], ppos[MAXN];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/ 
struct Node {
    int l, r, h, ty, hh;
    bool operator < (const Node &x) const {
        if(h == x.h && ty == x.ty) return r < x.r;
        if(h == x.h) return ty > x.ty;
        return h < x.h;
    }
}line[MAXN*3];
struct NODE {
    int pos, lhh, rhh, high;
    bool operator < (const NODE &x) const {
        return pos < x.pos;
    }
};
struct node {
    int v, c; node *next, *rev;
}pool[MAXN*1000], *h[MAXN<<5], *cur[MAXN<<5];
set <NODE> s;
inline void addedge(int u, int v, int c) {
    node *p = &pool[ecnt++], *q = &pool[ecnt++];
    *p = node{v, c, h[u], q}, h[u] = p;
    *q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
    tot = max(tot, u);
    if(l == r) {
        ppos[l] = u;
        return ; 
    }
    int mid = (l+r)>>1;
    buildx(u<<1, l, mid);
    buildx(u<<1|1, mid+1, r);
    addedge(u<<1, u, 10000);
    addedge(u<<1|1, u, 10000);
}
void buildy(int u, int l, int r) {
    if(l == r) {
        ppos[l] = u;//addedge(u+tot, l+n, 1);
        return ;
    }
    int mid = (l+r)>>1;
    buildy(u<<1, l, mid);
    buildy(u<<1|1, mid+1, r);
    addedge(u+tot, (u<<1)+tot, 10000);
    addedge(u+tot, (u<<1|1)+tot, 10000);
}
void find(int u, int id, int l, int r, int tl, int tr) {
    if(tl <= l && r <= tr) {
        interval[id][++Cnt[id]] = u;
        return ;
    }
    int mid = (l+r)>>1;
    if(tl <= mid) find(u<<1, id, l, mid, tl, tr);
    if(mid < tr) find(u<<1|1, id, mid+1, r, tl, tr);
}
inline void link(int l1, int r1, int l2, int r2) {
    if(l1 > r1 || l2 > r2) return ;
    //printf("Link %d %d %d %d\n", l1, r1, l2, r2);
    Cnt[0] = Cnt[1] = 0;
    find(1, 0, 1, n, l1, r1), find(1, 1, 1, n, l2, r2);
    for(int i = 1; i <= Cnt[0]; i++) 
        for(int j = 1; j <= Cnt[1]; j++)
            addedge(interval[0][i], interval[1][j]+tot, 10000);
}
inline bool bfs() {
    int front = 0, rear = 1;
    for(ri i = S; i <= T; i++) level[i] = 0;
    q[0] = S, level[S] = 1;
    while(front < rear) {
        int u = q[front++];
        if(u == T) return 1;
        for(node *p = h[u]; p; p = p->next) {
            if(p->c && !level[p->v]) {
                level[p->v] = level[u]+1;
                q[rear++] = p->v;
                if(p->v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u, int f) {
    if(u == T) return f;
    int F, flow = 0, MIN;
    for(node *&p = cur[u]; p; p = p->next) {
        if(p->c && level[p->v] == level[u]+1) {
            MIN = min(p->c, f-flow);
            F = dfs(p->v, MIN);
            p->c -= F, p->rev->c += F, flow += F;
            if(flow == f) return flow;
        }
    }
    return flow;
}
int Dinic() {
    int ret = 0;
    while(bfs()) {
        for(ri i = S; i <= T; i++) cur[i] = h[i];
        //if(double(clock())/CLOCKS_PER_SEC)
        ret += dfs(S, 30000);
    }
    return ret;
}
int X1[50], X2[50], Y1[50], Y2[50];
int main()
{
    int x1, y1, x2, y2;
    scanf("%d%d", &n, &Q);
    for(ri i = 1; i <= Q; i++) {
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        //if(i <= 42) X1[i] = x1, Y1[i] = y1, X2[i] = x2, Y2[i] = y2;
        line[++cnt] = Node{x1, x2, y1, 1, 0};
        line[++cnt] = Node{x1, x2, y2, 0, y1};
    }
    buildx(1, 1, n);
    //cout<<tot<<' ';
    for(ri i = 1; i <= n; i++) addedge(S, ppos[i], 1);
    buildy(1, 1, n);
    T = tot*2+1;
    for(ri i = 1; i <= n; i++) addedge(ppos[i]+tot, T, 1);
    line[++cnt] = Node{1, n, n+1, 1, 0};
    line[++cnt] = Node{0, 0, n+2, 0, 0};
    sort(line+1, line+cnt+1);
    s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
    int l, r, ty, h, lh, rh, pos = 1, pos2 = 1, curr = -1;
    NODE L, R;
	for(ri i = 1; i <= n+1; i++) { 
        //cout<<i<<'\n';
        curr = -1;
        while(line[pos].h <= i && line[pos].ty) {
            l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
            s.insert(NODE{l, h, h, line[pos].hh}), s.insert(NODE{r, h, h, line[pos].hh});
            pos++;
            L = *--s.lower_bound(NODE{l, 0, 0}), R = *++s.lower_bound(NODE{r, 0, 0});
            if(R.pos <= curr) continue;
            curr = R.pos;
            //printf("%d %d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos, curr);
            link(max(L.rhh, R.lhh), h-1, L.pos+1, R.pos-1);
        }
        pos2 = pos;
        while(line[pos].h <= i) {
            l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
            L = *--s.lower_bound(NODE{l, 0, 0}), R = *++s.lower_bound(NODE{r, 0, 0});
            lh = max(L.rhh, line[pos].hh), rh = max(R.lhh, line[pos].hh);
            //printf("%d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos);
            //cout<<L.pos<<' '<<L.hh<<' '<<line[i].hh<<' '<<l<<' '<<r<<' '<<R.pos<<endl; 
            link(lh, h, L.pos+1, l-1);
            if(R.high != i) link(rh, h, r+1, R.pos-1);
            pos++;
            int lh1 = L.lhh, lh2 = L.rhh, rh1 = R.lhh, rh2 = R.rhh; 
            int lp = L.pos, rp = R.pos, lhi = L.high, rhi = R.high;
            s.erase(L), s.insert(NODE{L.pos, lh1, max(h+1, lh2), lhi});
            s.erase(R), s.insert(NODE{R.pos, max(h+1, rh1), rh2, rhi});
        }
        while(line[pos2].h <= i) {
            l = line[pos2].l, r = line[pos2].r, ty = line[pos2].ty, h = line[pos2].h;
            s.erase(NODE{l, line[pos2].hh, 0}), s.erase(NODE{r, line[pos2].hh, 0});
            pos2++;
        }
    }
    printf("%d\n", Dinic());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值