洛谷 P12031 [USACO25OPEN] Forklift Certified P

题目链接

对于 \(M = 1\),不能直接拓扑排序,考虑到真正有用的边只有 \(\text O (n)\) 条,可以 dfs 找拓扑序,也就是说在 dfs 时如果找到一个还没被访问过的儿子则先递归儿子,把所有儿子都加入拓扑序后再加入自己。

快速找儿子的过程可以用线段树维护,刚开始以 \(x\) 为下标,把所有 \((x _ {i1}, y _ {i1})\) 加入线段树,每次访问到一个点就单点修改坐标 \(x _ {i1}\)\(+\infty\),前缀查询 \([1, x _ {i2}]\) 最小值再和 \(y _ {i2}\) 比较即可。

对于 \(M = 2\),可以倒序加入,然后树状数组 / 线段树找是否存在儿子即可。

时间复杂度 \(\text O (n \log n)\)

#include<cstdio>
#include<algorithm>
#define N 1000005
#define y1 homo
using namespace std;

const int inf=0x3f3f3f3f;
int T,cxk,n,x1[N],y1[N],x2[N],y2[N];
struct segtree {
	struct st {
		int l,r,mn;
		#define l(p) tr[p].l
		#define r(p) tr[p].r
		#define mn(p) tr[p].mn
	} tr[N*4];
	void build(int p,int l,int r) {
		l(p)=l,r(p)=r,mn(p)=inf;
		if(l==r) return;
		int mid=l+r>>1;
		build(p*2,l,mid),build(p*2+1,mid+1,r);
	}
	void insert(int p,int x,int c) {
		if(l(p)==r(p)) {mn(p)=c; return;}
		insert(p*2+(x>r(p*2)),x,c);
		mn(p)=min(mn(p*2),mn(p*2+1));
	}
	int ask(int p,int l,int r) {
		if(l<=l(p)&&r>=r(p)) return mn(p);
		int res=inf;
		if(l<=r(p*2)) res=min(res,ask(p*2,l,r));
		if(r>=l(p*2+1)) res=min(res,ask(p*2+1,l,r));
		return res;
	}
} t1;
namespace sub1 {
	int p[N];
	bool used[N];
	void cst(int x) {
		used[x]=1,t1.insert(1,x1[x],inf);
		int y=t1.ask(1,1,x2[x]);
		while(y<y2[x]) cst(p[y]),y=t1.ask(1,1,x2[x]);
		printf("%d ",x);
	}
	void solve() {
		for(int i=1;i<=n;i++) used[i]=0,t1.insert(1,x1[i],y1[i]),p[y1[i]]=i;
		for(int i=1;i<=n;i++) if(!used[i]) cst(i);
		puts("");
	}
}
namespace sub2 {
	bool ans[N];
	void solve() {
		for(int i=n;i;i--) ans[i]=t1.ask(1,1,x2[i])>y2[i],t1.insert(1,x1[i],y1[i]);
		for(int i=1;i<=n;i++) putchar('0'+ans[i]);
		puts("");
	}
}
int main() {
	scanf("%d%d",&T,&cxk);
	while(T--) {
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i]);
		t1.build(1,1,n*2);
		if(cxk==1) sub1::solve();
		else sub2::solve();
	}
	return 0;
}
posted @ 2026-01-06 19:11  yemuzhe  阅读(6)  评论(0)    收藏  举报