【2020.11.14NOIP普及组模拟】到达 题解

题目描述

有n 个在二维平面上,第i 个点的坐标是xi, yi。这里x1, x2, …, xn 和y1, y2, …, yn都是1 到n 的排列。
对于任何一个点k,我们可以走到横纵坐标都比它大的点,或者横纵坐标都比它小的点。
现在,我们想要知道当k = i 的时候,有多少个点可以走到第k 个点。

输入

输入的第一行是一个正整数n,表示平面上点的数量。1 ≤ n ≤ 200000。
接下来n 行,每行有2 个正整数,表示xi, yi。

输出

输出共计有n 行n 个答案,对于第i 行,输出有多少个点可以到达第i 个点(包
括自己)。

样例输入

【输入样例1】

4
1 4
2 3
3 1
4 2

【输入样例2】

7
6 4
4 3
3 5
7 1
2 7
5 2
1 6

样例输出

【输出样例1】

1
1
2
2

【输出样例2】

3
3
1
1
2
3
2

解题:

题目大意:

给出 n n n个点,每个点的 x x x y y y都是 n n n的排列。每个点都可以走向 x x x y y y都比它大的点。求当 k = i k = i k=i 的时候,有多少个点可以走到第 k k k 个点。

20%:

有没有发现这道题似曾相识,好像有点像 树状数组 星星点灯,于是我就打了 1 h 1h 1h+的时间来打树状数组,结果样例2过不去。
仔细一想,点不一定要直接走到点,还可以间接走。如:

A(1,6)-->>B(3,7) 
B(3,7)-->>C(2,5)
A(1,6)-/>>C(2,5)
A(1,6)-->>B(3,7)-->>C(2,5)

50%:

有了上面的例子,我们自然就想到,若是有几个点互相连通,那它们的值就是点的个数,于是并查集优化就出现了。
时间复杂度: O ( n 2 ) O(n^2) On2

代码:

#include<cstdio>
#define Fu(i,a,b) for(int i=(a);i<=(b);i++)
#define Fd(i,a,b) for(int i=(a);i>=(b);i--)
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
int fa[200005],n,x[200005],y[200005],bj[200005];
int find(int x){
    
    
	if(fa[x]!=x)return fa[x]=find(fa[x]);
	return x;
}
int main(){
    
    
	fre(arrive);
	scanf("%d",&n);
	Fu(i,1,n){
    
    
		fa[i]=i;
		scanf("%d%d",&x[i],&y[i]);
	}
	Fu(i,1,n){
    
    
		Fu(j,1,i-1){
    
    
			if((x[i]>x[j]&&y[i]>y[j])||(x[i]<x[j]&&y[i]<y[j])){
    
    
				fa[find(i)]=find(j);
			}
		}
	}
	Fu(i,1,n){
    
    
		bj[find(i)]++;
	}
	Fu(i,1,n)printf("%d\n",bj[find(i)]);
	return 0;
}

100%:

有了20和50分思路的铺垫,满分自然就出来了,50分的问题在于判断联通时用了 O ( n 2 ) O(n^2) On2,所以,我们就可以用单调栈来优化。
单调栈的处理比较简单,但又很多细节:

  1. x x x排序,只看 y y y
  2. 第一遍求左下角( x x x为升序),维护栈的单调递增
  3. 第二遍求左下角( x x x为降序),维护栈的单调递减
  4. 每次出栈的都合并成一个连通块
  5. 记录输出(细节,要多开两个数组)

代码:

#include<cstdio>
#include<iostream>
#define Fu(i,a,b) for(int i=(a);i<=(b);i++)
#define Fd(i,a,b) for(int i=(a);i>=(b);i--)
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;
int fa[200005],n,x[200005],y[200005],w[200005],bj[200005];
int z[200005],size,v[200005];
void qsort(int l,int r){
    
    
	int i=l,j=r,mid=x[(l+r)/2];
	while(i<=j){
    
    
		while(x[i]<mid) i++;
		while(x[j]>mid) j--;
		if(i<=j) swap(x[i],x[j]),swap(y[i],y[j]),swap(w[i],w[j]),i++,j--;
	}
	if(l<j) qsort(l,j);
	if(i<r) qsort(i,r);
}
int find(int x){
    
    
	if(fa[x]!=x)return fa[x]=find(fa[x]);
	return x;
}
int main(){
    
    
	fre(arrive);
	scanf("%d",&n);
	Fu(i,1,n){
    
    
		fa[i]=i,w[i]=i;
		scanf("%d%d",&x[i],&y[i]);
	}
	qsort(1,n);
	Fu(i,1,n){
    
    
		while(y[z[size]]<y[i]&&size>0){
    
    
			fa[find(z[size])]=find(i);
			size--;
		}
		z[++size]=i;
	}
	
	size=0;
	Fd(i,n,1){
    
    
		while(y[z[size]]>y[i]){
    
    
			fa[find(z[size])]=find(i);
			size--;
		}
		z[++size]=i;
	}
	Fu(i,1,n) bj[find(i)]++;
	Fu(i,1,n)v[w[i]]=bj[find(i)];
	Fu(i,1,n) printf("%d\n",v[i]);
	return 0;
}

一道水题的题解。

猜你喜欢

转载自blog.csdn.net/zhy_Learn/article/details/109727676
今日推荐