jzoj3578 【CEOI2013】Adriatic 巧妙dp

来源:互联网 发布:淘宝店怎么找上家 编辑:程序博客网 时间:2024/05/19 13:55

题意

平面上若干个点,每个点(x,y)可以到达满足x0<x,y0<yx0>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);    }}
原创粉丝点击