Solution -「POJ 1057」Chessboard

cirnovsky /

link。

调起来真的呕吐,网上又没篇题解。大概是个不错的题。

首先行和列一定是独立的,所以我们把行列分开考虑。这样的问题就弱化为:在一个长度为 nn 的格子带上,有 nn 个物品,每个物品 xx 对应一个区间 [lx,rx][l_x,r_x],分配每个物品的居所使得各住各的,求出其中的固定点。

把物品放在左部,把格子放在右部,即构造一个二部图。那么问题就是求出其最大匹配的必要边。先考虑如何求这个的最大匹配,这是个经典贪心吧,把每个区间按 ll 排序,然后枚举位置,优先填入 rr 小的物品。跑完后(即规定好方向后)对整个图跑缩点,两端点不在同一连通块的边即为必要边。

这个的边数是 O(n2)O(n^2) 的,因为连边一下连一个区间,考虑利用这个来优化。线段树的高度不高吧,而且能够用来刻画一个区间,于是用这个东西来优化连边。

具体一点是,线段树上父亲对两个儿子连边,物品就对线段树上自己的区间连边即可。注意清空的时候带脑子……

#include<bits/stdc++.h>
using namespace std;
#define cmin(x, y) x = min(x, y)
#define cmax(x, y) x = max(x, y)
void eof(const char IO) {if(IO == -1) exit(0);}
template<typename T=int> T read() {
	T x=0; char IO=getchar(); bool f=0; eof(IO);
	while(IO<'0' || IO>'9')	f|=IO=='-',eof(IO=getchar());
	while(IO>='0' && IO<='9')	x=x*10+(IO&15),eof(IO=getchar());
	return f?-x:x;
}
int n,A[100100],B[100100],C[100100],D[100100],rec1[100100],rec2[100100],tot,ans1[100100],ans2[100100];
int col[400100],dfn[400100],low[400100],dfsnt,colnt,sta[400100],top,mat[400100],inst[400100];
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
struct node{
	int l,r,id;
	friend bool operator<(const node& x,const node& y) {return x.l < y.l;}
} cur[100100];
vector<node> ans;
vector<vector<int>> e;
void DFS(const int now) {
	inst[sta[++top] = now] = 1;
	dfn[now] = low[now] = ++dfsnt;
	for(const int y : e[now]) {
		if(!dfn[y]) DFS(y),cmin(low[now], low[y]);
		else if(inst[y]) cmin(low[now], dfn[y]);
	}
	if(low[now]^dfn[now]) return;
	colnt++;
	int y; do {
		inst[y = sta[top--]] = 0;
		col[y] = colnt;
	} while(y != now);
}
#define mem(x, w, n) memset(x, w, n)
void sweep() {
	e.clear();
	mem(col, 0, min(size_t(n*4*30), sizeof(col)));
	mem(dfn, 0, min(size_t(n*4*30), sizeof(dfn)));
	mem(low, 0, min(size_t(n*4*30), sizeof(low)));
	mem(sta, 0, min(size_t(n*4*30), sizeof(sta)));
	mem(inst, 0, min(size_t(n*4*30), sizeof(inst)));
	mem(mat, 0, min(size_t(n*4*30), sizeof(mat)));
	// puts("DEBUGGING ----------");
	// for(int* i : {col, dfn, low, sta, inst}) {
	// 	for(int j = 0;  j < 233; ++j) printf(" %d",i[j]);
	// 	puts("");
	// }
	// puts("END___D__D__D____D___D_");
	tot = dfsnt = colnt = top = 0;
	while(q.size()) q.pop();
}
int tr[400100],reid[400100];
void addedge(const int one, const int ano) {
	// printf("add:%d %d\n",one,ano);
	if(one >= int(e.size())) e.resize(one+1);
	e[one].push_back(ano);
}
void build(const int now, int l, int r) {
	// printf("  %d  %d\n",l,r);
	tr[now] = ++tot;
	if(l == r) return reid[l] = tot,void();
	int mid = (l+r)>>1;
	build(now<<1, l, mid),build(now<<1|1, mid+1, r);
	addedge(tr[now], tr[now<<1]),addedge(tr[now], tr[now<<1|1]);
}
void lin(int x,int y,int k,const int now = 1,int l = 1,int r = n) {
	if(l>y || r<x || x>y) return;
	if(l>=x && r<=y) return addedge(k, tr[now]),void();
	int mid = (l+r)>>1;
	lin(x, y, k, now<<1, l, mid),lin(x, y, k, now<<1|1, mid+1, r);
}
bool solve(int rec[], int ans[]) {
	sweep();
	static int l[100100], r[100100];
	for(int i=1; i<=n; ++i) l[i] = cur[i].l,r[i] = cur[i].r;
	sort(cur + 1, cur + n + 1);
	for(int i=1, p=1; i<=n; ++i) {
		while(p <= n && cur[p].l <= i) q.emplace(cur[p].r, cur[p].id),p++;
		if(!q.size() || q.top().first<i) return 0;
		mat[q.top().second] = i;
		q.pop();
	}
	tot = n;
	build(1, 1, n);
	// printf("  tot: %d\n", tot);
	// for(int i=1; i<=4*n; ++i) printf(" %d",tr[i]);
	// puts("");
	for(int i=1; i<=n; ++i) {
		addedge(reid[mat[i]], i);
		lin(l[i], mat[i]-1, i);
		lin(mat[i]+1, r[i], i);
	}
	for(int i=1; i<=tot; ++i) if(!dfn[i]) DFS(i);
	// for(int i=1; i<=tot; ++i) printf(" %d",col[i]);
	for(int i=1; i<=n; ++i) rec[i] = col[i] != col[reid[mat[i]]],ans[i] = mat[i];
	return 1;
}
signed main() {
	while(n = read(),233) {
		for(int i=1; i<=n; ++i) A[i] = read(),B[i] = read(),C[i] = read(),D[i] = read();
		for(int i=1; i<=n; ++i) cur[i] = (node){A[i], C[i], i};
		if(!solve(rec1, ans1)) {
			puts("-1");
			goto Fail;
		}
		for(int i=1; i<=n; ++i) cur[i] = (node){B[i], D[i], i};
		if(!solve(rec2, ans2)) {
			puts("-1");
			goto Fail;
		}
		vector<node>().swap(ans);
		for(int i=1; i<=n; ++i) if(rec1[i] && rec2[i]) ans.push_back((node){i, ans1[i], ans2[i]});
		cout<<ans.size()<<endl;
		for(const auto [x, y, z] : ans) printf("%d %d %d\n", x, y, z);
		Fail:;
	}
	return 0;
}