题解 P6283 【[USACO20OPEN] The Moo Particle S】
写在前面的话
苏格拉底曾指出,求得知识的最好办法是有系统的问与答,故本题解采用了该种方式。
思路分享
问:这道题你怎么看?
答:首先看到这道题时,我们可能会想到二位偏序,但是很遗憾地告诉大家,这道题用二位偏序并不能解决。
问:为什么不能解决呢?
答:考虑对于
问:那么我们该怎么做呢?
答:考虑转化为一道图论题,若粒子
问:为什么要这么做呢?
答:因为我们可以发现,若一些粒子处于一个连通块中,那么这些粒子就可以经过一番相互作用之后变为
问:但是这样仍是
答:考虑使用二位偏序的思想,将这些点以
问:那么如何计算答案呢?
答:考虑使用并查集的思想,若排序后,一个粒子可以与它前面的粒子相互作用,那么就将这两个点连接起来,否则就计算一次贡献。
问:乍一看感觉很对呢,这就是正解了吧?
答:先别着急,我们先来看看这个思路的代码实现。
#include<bits/stdc++.h>
using namespace std;
const int maxn=100100;
int n,Min;
struct node{
int x,y;
bool operator<(node u)const{
return y==u.y?x>u.x:y<u.y;
}//x坐标需要从大到小排序
//因为如果两个粒子y坐标相同,那么x较大的更有可能与其他粒子互相作用,从而获得更小答案。
}moo[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d%d",&moo[i].x,&moo[i].y);
}
stable_sort(moo+1,moo+1+n);
Min=1000000001;
moo[0].y=-1000000001;
int ans=0;
for(int i=1;i<=n;i++){
if(moo[i].x<Min&&moo[i].y!=moo[i-1].y){
ans++;//若与前面的粒子无法发生互相作用,那么将答案加1
}
Min=min(Min,moo[i].x);
}
printf("%d",ans);
return 0;
}
答:看完之后,可以发现其实这个方法是错误的,只能通过前两个测试点(也就是两个样例)
问:为什么它是错的呢?乍一看感觉很对呢。
答:我们可以考虑如下图中的三个粒子。
答:按照这种方式排好序之后,我们就可以得到三个粒子
问:那好吧。那么我们还可以怎样做呢?
答:我们不妨设
问:那么这就是正解了吗?
答:是的。
正解代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=100100;
int n;
int l[maxn],r[maxn];
struct node{
int x,y;
bool operator<(node u)const{
return y==u.y?x<u.x:y<u.y;
}//与前面差不多的排序,但是y坐标相同时x坐标需改成从小到大
}moo[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d%d",&moo[i].x,&moo[i].y);
}
stable_sort(moo+1,moo+1+n);
l[1]=moo[1].x;
for(int i=2;i<=n;i++){
l[i]=min(l[i-1],moo[i].x);
}//处理l
r[n]=moo[n].x;
for(int i=n-1;i>=1;i--){
r[i]=max(r[i+1],moo[i].x);
}//处理r
int ans=1;//这里ans的初始值需要赋为1
for(int i=1;i<n;i++){
if(l[i]>r[i+1]){
ans++;
}
}
printf("%d",ans);
return 0;
}