题意
平面上若干个点,每个点(x,y)可以到达满足
x0<x,y0<y或x0>x,y0>y
的点(x0,y0),问对于每一个点,所有点到它的最小步数和. (保证点两两互达)
3<=N<=250 000,0<坐标<=2500
分析
考虑到一个点将会把图分成四份区域。其中两个区域(左下,右上)是可以直接到达x的,那这个贡献就好算. 我们要算的只是另外两个区域.
两个区域差不多,只讲一个好了(左上)
画个图发现,左上的点必然会先跑进左下,右上,然后才能进点x.
于是我们设f[i][j]表示,一个以(i,j)为右下角的左上区域,他里面的所有点跑进左下,右上的最小代价。
如何更新?
找到两个蓝色区域的严格更新线,也就是说,在图中两条虚线的左上角那个框框(x0,y0)里的所有点,都不能直接走到蓝色区域里.
然后,f[x][y]=f[x0][y0]+sum(x,y),sum(x,y)表示左上角白色区域iii里的点数.
因为虚线划分出来的白色区域需要再走一步才能走到黄色区域里(保证有解),所以这一区域里的点要算走出来的方案f[x0][y0]走到黄色区域,然后再走一步走到蓝色区域里.
不难发现用严格更新线构成的矩阵更新是最优的 (黄色区域里的一步肯定最优,小白色区域必须先走到黄色区域,再走出来,那就满足最优子结构)
左上右下都做一次,答案很好算
#include <cstdio>
#include <iostream>
#include <cstring>
#define can(x,y) ((zx[x]<zx[y] && zy[x]<zy[y]) || (zx[x]>zx[y] && zy[x]>zy[y]))
using namespace std;
const int N = 250010,M = 2510;
int zx[N],zy[N];
int n,lx,ly;
int Q[N],head,tail,dis[N],bz[N];
int minh[M],minl[M],maxh[M],maxl[M];
int f[M][M],f2[M][M],cnt[M][M];
int sum(int lx,int ly,int rx,int ry) {
return cnt[rx][ly] - cnt[rx][ry-1] - cnt[lx-1][ly] + cnt[lx-1][ry-1];
}
int main() {
freopen("3578.in","r",stdin);freopen("3578.out","w",stdout);
cin>>n;
memset(minh,127,sizeof minh);
memset(minl,127,sizeof minl);
for (int i=1; i<=n; i++) {
scanf("%d %d",&zx[i],&zy[i]);
cnt[zx[i]][zy[i]]++;
lx=max(lx,zx[i]),ly=max(ly,zy[i]);
minh[zx[i]]=min(minh[zx[i]],zy[i]);
maxh[zx[i]]=max(maxh[zx[i]],zy[i]);
minl[zy[i]]=min(minl[zy[i]],zx[i]);
maxl[zy[i]]=max(maxl[zy[i]],zx[i]);
}
for (int i=1; i<=lx; i++) for (int j=1; j<=ly; j++)
cnt[i][j]+=cnt[i-1][j] + cnt[i][j-1] - cnt[i-1][j-1];
for (int i=lx; i; i--) maxh[i]=max(maxh[i],maxh[i+1]);
for (int i=ly; i; i--) maxl[i]=max(maxl[i],maxl[i+1]);
minl[0]=lx+1, minh[0]=ly+1;
for (int i=1; i<=lx; i++) minh[i]=min(minh[i],minh[i-1]);
for (int i=1; i<=ly; i++) minl[i]=min(minl[i],minl[i-1]);
for (int i=ly; i; i--) for (int j=1; j<=lx; j++) {
int topy=max(i,maxh[j+1]),topx=min(j,minl[i-1]);
f[j][i]=f[topx][topy] + sum(1,ly,j,i);
}
for (int i=1; i<=ly; i++) for (int j=lx; j; j--) {
int topy=min(i,minh[j-1]),topx=max(j,maxl[i+1]);
f2[j][i]=f2[topx][topy] + sum(j,i,lx,1);
}
for (int i=1; i<=n; i++) {
printf("%d\n",f[zx[i]][zy[i]] + f2[zx[i]][zy[i]] + n - 3);
}
}