CF576C Points on Plane 题解
这道题是一道构造题,很有 CF 的风格。
首先,观察 \(x,y \leq 10^6\) ,不难想到一个思路:排序, \(x\) 为第一关键字, \(y\) 为第二关键字。但显然这样是错的。因为我们只要构造一组跨度足够大的数据,就可以把这个做法卡掉。
但是,排序 的思路是值得我们探讨的,只是如何排序的问题。
想一想,我们要使得曼哈顿距离之和最小,那么我们就需要将这些点 划分成几个部分,使得每一个部分 内部距离和足够小 ,并且几个部分之间 移动的距离也需要足够小。
所以,根据上述分析,我们能想到什么?莫队!
我们可以把二维坐标 \((x,y)\) 看作一维区间 \([l,r]\) ,将相邻两个点的移动变成区间的移动,要使区间移动次数尽量小,这不是莫队经常干的事吗qwq。
不过:如果我们按照常规莫队的套路(按照左端点所在块数为第一关键字,右端点为第二关键字),会发现只要构造这样一组数据就会 WA:
假设此时坐标已经排过序了:
1e3,0
1e3,1
1e3,2
...
1e3,1e6
2e3,0
...
2e3,1e6
3e3,0
...
3e3,1e6
4e3,0
...
这样,本来可以每一个点只走一次,但是除了 \(x=10^3\) 的点由于莫队的排序方式都被走了两次,就会导致路程浪费,得不偿失。
所以,这个地方需要加入一个常见的莫队优化(之所以说是常见是因为在很多莫队算法讲解的博客中都讲到了这个优化):假设当前左端点在第 \(b\) 块,如果 \(b\) 为奇数,那么右端点从小到大排序,否则从大到小排序。这样,我们就能完美解决上述问题。
由于英文题面中有 It is guaranteed that the answer exists.
,所以无需考虑无解情况。
时间复杂度: \(O(nlogn)\),一次快排即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
int n,block;
struct node
{
int l,r,id,b;
}que[MAXN];
int read()
{
int sum=0,fh=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') fh=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {sum=(sum<<3)+(sum<<1)+(ch^48);ch=getchar();}
return sum*fh;
}
bool cmp(const node &fir,const node &sec)
{
if(fir.b!=sec.b) return fir.b<sec.b;
if(fir.b&1) return fir.r<sec.r;
return fir.r>sec.r;
}
int main()
{
n=read();block=1000;//此处设块长为 1000,无需担心被上述反例卡掉(各位读者可以自行计算是否会被卡掉)
for(int i=1;i<=n;i++) {que[i].l=read();que[i].r=read();que[i].id=i;que[i].b=(que[i].l-1)/block+1;}
sort(que+1,que+n+1,cmp);
for(int i=1;i<=n;i++) cout<<que[i].id<<" ";
cout<<"\n";
return 0;
}