洛谷 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;
}

浙公网安备 33010602011771号