题目描述
有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) O(n2)。
代码:
#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) O(n2),所以,我们就可以用单调栈来优化。
单调栈的处理比较简单,但又很多细节:
- 按 x x x排序,只看 y y y
- 第一遍求左下角( x x x为升序),维护栈的单调递增
- 第二遍求左下角( x x x为降序),维护栈的单调递减
- 每次出栈的都合并成一个连通块
- 记录输出(细节,要多开两个数组)
代码:
#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;
}
一道水题的题解。