怎么没有人写 CDQ 分治,模拟赛上用 CDQ 过了这题。

假若一个点并另一个点更重要就连一条边,不难发现只要按照拓扑序简单 即可。

考虑全部按照第一维排序,那么在 CDQ 分治的时候就解决了第一维的限制,在 CDQ 的过程中对左右两边的递归区间处理好按照第二维排序的结果,并枚举左边的点,能连向的右边的点一定是一段前缀,前缀优化建图处理。

通过精细的实现可以做到 考场上随便写的 做法。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6+114;
int n,tot;
vector<int> e[maxn];
struct Point{
	int x,y,id;
}P[maxn];
bool cmpx(Point x,Point y){
	return x.x<y.x;
}
bool cmpy(Point x,Point y){
	return x.y<y.y;
}
int pre[maxn];//记录下当前建立的前缀节点的编号 
void CDQ(int l,int r){
	if(l==r) return ;
	int mid=(l+r)>>1;
	CDQ(l,mid);
	CDQ(mid+1,r);
	//这里转变为对 y 排序
	sort(P+l,P+mid+1,cmpy);//l - mid
	sort(P+mid+1,P+r+1,cmpy);//mid+1 - r
	//可以用归并做到单 log 但是有点麻烦就算了
	for(int i=l;i<=mid;i++){
		pre[i]=++tot;
		if(i!=l) e[pre[i]].push_back(pre[i-1]);
		e[pre[i]].push_back(P[i].id);
	} 
	int now=l-1;
	for(int i=mid+1;i<=r;i++){
		while(P[now+1].y<P[i].y&&now<mid) now++;
		if(now!=l-1){
			e[P[i].id].push_back(pre[now]);
		}
	}
	return ;
}
int d[maxn],dis[maxn];
void topo(){
	for(int i=1;i<=tot;i++){
		for(int v:e[i]){
			d[v]++;
		}
		dis[i]=0;
	}
	queue<int> q;
	for(int i=1;i<=tot;i++){
		if(d[i]==0){
			if(i<=n) dis[i]=1;
			q.push(i);
		}
	}
	while(q.size()>0){
		int u=q.front();
		q.pop();
		for(int v:e[u]){
			dis[v]=max(dis[v],dis[u]);
			d[v]--;
			if(d[v]==0){
				if(v<=n) dis[v]++;
				q.push(v);
			}
		}
	}
	return ;
}
int main(){
	//freopen("t2.in","r",stdin);
	//freopen("t2.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>P[i].x>>P[i].y,P[i].id=i;
	tot=n;
	sort(P+1,P+n+1,cmpx);
	CDQ(1,n);
	topo();
	for(int i=1;i<=n;i++){
		cout<<dis[i]<<'\n';
	} 
	return 0;
}
/*
5 
1 4 
2 2 
3 3 
4 1 
5 5
*/
```cpp