##题面
链接_洛谷
链接_CF
简述一下题意:
在一个
n
∗
n
(
n
≤
10000
)
n*n(n\leq10000)
n∗n(n≤10000)的棋盘上放棋子,要求一行一列不能放两个棋子。现在删去
n
n
n个矩形,输入方式是给左下坐标和右上坐标。删去的矩形不能放棋子,但棋子隔着一个删去的矩形仍然会互相干扰。求最多放几个棋子。
##分析
首先考虑放棋子是什么意思(这是一个挺常见的转化),一行和一列去配对,每一行每一列只能配对一次。删去的部分就是这些行和这些列不可以配对。
连10000*10000条边显然要超时炸空间,那么我们考虑线段树优化建图。不了解优化建图的建议出门百度网络流优化建图。但是这个时候遇到一个问题,就是优化建图是一段区间向一段区间连边,但是我们只知道一段区间不能和一段区间连边。所以考虑如何把剩余部分划分成若干矩形。
一种十分容易想到的方式是直接从所有删掉的矩形的边那里切开(如图,红色是删掉的部分),然而如果删掉的是一条对角线上的所有矩形这就变暴力了TAT。
我们观察一下这个切分方式:我们切分了很多无用的线,对于在它上面很多的矩形切下来的线,完全可以置之不理。
于是我们考虑扫描线。具体步骤如下:对于每一个矩形的下边,它以下部分切一个矩形出来。对于一个矩形的上边,左右两侧切两个矩形出来。如图。
现在来考察复杂度:对于每个矩形,遇到底边的时候出现一个矩形,顶边的时候出现两个矩形,于是我们一共会剖分
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;
}