题解:P12031 [USACO25OPEN] Forklift Certified P

· · 题解

思路

首先一个矩形A阻挡另一个矩形B当且仅当A的左下端点在B的右上端点的左下角。

那么显然有一个暴力的做法:将阻挡关系作为一条有向边,M=1 就是求拓扑序,M=2 就是删除前面点后当前点入度是否为 0。

该方法复杂度 O(n^2),瓶颈在于有向边的数量。

我们考虑不建边,用 dfs 来求拓扑序。

由于矩形左下角的矩形是一个一个处理,我们可以用一维线段树:以 x 坐标为下标,y 坐标作为值,每次求 1 \sim x_2 的最小值,如果 \ge y_2 就代表已经删完,否则就删最小值所对应的矩形,然后继续处理。因为每个矩形只删一次,复杂度是 O(n\log n) 的。

## 代码 ``` #include<bits/stdc++.h> using namespace std; #define pii pair<int,int> #define mid ((l+r)>>1) #define ls (p<<1) #define rs ((p<<1)|1) #define lson ls,l,mid #define rson rs,mid+1,r const int N=2e5+5,inf=0x3f3f3f3f; struct node{int x1,x2,y1,y2;}a[N]; int T,n,M; bool del[N],vis[N]; vector<int> ans; pii tr[N<<2]; pii merge(pii x,pii y){return x.first<y.first?x:y;} void build(int p,int l,int r){ tr[p]={inf,0}; if(l!=r) build(lson),build(rson); } void update(int p,int l,int r,int x,pii v){ if(l==r){tr[p]=v;return;} x<=mid?update(lson,x,v):update(rson,x,v),tr[p]=merge(tr[ls],tr[rs]); } pii query(int p,int l,int r,int L,int R){ if(L<=l&&r<=R) return tr[p]; if(L<=mid&&R>mid) return merge(query(lson,L,R),query(rson,L,R)); return L<=mid?query(lson,L,R):query(rson,L,R); } void dfs(int u){ if(del[u]) return; if(!vis[u]) update(1,1,2*n,a[u].x1,{inf,0}),vis[u]=1;//删除 pii v=query(1,1,2*n,1,a[u].x2); if(v.first>=a[u].y2){del[u]=1,ans.push_back(u);return;} dfs(v.second),dfs(u); } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>T>>M; while(T--){ cin>>n;build(1,1,2*n);ans.clear(); for(int i=1;i<=n;i++){cin>>a[i].x1>>a[i].y1>>a[i].x2>>a[i].y2;update(1,1,2*n,a[i].x1,{a[i].y1,i}),vis[i]=del[i]=0;} if(M==1){ for(int i=1;i<=n;i++) if(!del[i]) dfs(i); for(int i=0;i<ans.size();i++) cout<<ans[i]<<" "; }else{ for(int i=1;i<=n;i++){ update(1,1,2*n,a[i].x1,{inf,0}); cout<<(query(1,1,2*n,1,a[i].x2).first>=a[i].y2); } } cout<<"\n"; } return 0; }