bzoj3341: [Ceoi2013]adriatic 记忆化搜索

题意:给你一个矩阵,上面有一些点,两个点之间能直接互相到达保证两个点的坐标一定严格遵从相同的大小性 ax<bx,ay<by)||(ax>bx,ay>by) ,输出每一个点到所有店的最短路径之和。n<=250000. 矩阵长宽<=2500.
一开始还以为是什么奇怪的扫描线之类的,或者cdq。。想了想不大可能啊,性质不是很符合,然后不大会了。。考场上直接写了个暴力。。只不过暴力的数据好像很强的样子。。
正解很玄妙。
首先假设我们当前处理到第i个点,考虑如何计算他的贡献。
以i为原点画坐标系,第2,4象限的点明显直接到达。
然后考虑计算1,3象限的点,显然处理的方法类似,所以我们只讨论如何处理第一象限的点。
那么可以发现第一象限的点要到达原点,只能是:象限2->象限1->象限4->原点。
那么我们对于每一个第一象限的点再次划分出坐标系,计算出左上角的点个数以后算出右下角的点数即可,所以创出新的坐标系时只需要以最右最上的点为中心继续拓展即可,其余的在他下面的都会被包括。以此类推。然而直接递归做会超,所以记忆化搜索就好。
所以我们需要预处理出前缀和和前缀最小/大横坐标以及前缀最小/大纵坐标,即可。

有点分治的味道。看了题解觉得简单。。然而考场上做出来的人并不多(几乎没有。。)

#include
#include
#include
#include 
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=2505;
const int M=1e5+5;
const int inf=1e9+5;
int n,m;
typedef long long ll;
int bo[N][N];
int head[N],next[N],go[N];
struct node
{
    int x,y;
}a[M*10];
int sum[N][N],mnx[N][N],mny[N][N];
int mxx[N][N],mxy[N][N];
int ur[N][N],dl[N][N];
inline int calur(int x,int y)
{
    if (ur[x][y]!=-1)return ur[x][y];
    if (sum[x][2500]-sum[x][y-1]==0)return ur[x][y]=0;
    ur[x][y]=sum[x][2500]-sum[x][y-1]+calur(min(x,mnx[x][y-1]),max(y,mxy[x+1][y]));
    return ur[x][y];
}
inline int caldl(int x,int y)
{
    if (dl[x][y]!=-1)return dl[x][y];
    if (sum[2500][y]-sum[x-1][y]==0)return dl[x][y]=0;
    dl[x][y]=sum[2500][y]-sum[x-1][y]+caldl(max(x,mxx[x][y+1]),min(y,mny[x-1][y]));
    return dl[x][y];
}
inline void pre()
{
    fo(i,1,2500)
    {
        fo(j,1,2500)
        {
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+bo[i][j];
            if (bo[i][j])
            {
                mnx[i][j]=min(i,mnx[i-1][j]);
                mny[i][j]=min(j,mny[i][j-1]);
            }
            else
            {
                mnx[i][j]=min(mnx[i][j-1],mnx[i-1][j]);
                mny[i][j]=min(mny[i][j-1],mny[i-1][j]);
            }
        }   
    }   
    fd(i,2500,1)
    {
        fd(j,2500,1)
        {
            if (bo[i][j])
            {
                mxx[i][j]=max(i,mxx[i+1][j]);
                mxy[i][j]=max(j,mxy[i][j+1]);
            }
            else
            {
                mxx[i][j]=max(mxx[i+1][j],mxx[i][j+1]);
                mxy[i][j]=max(mxy[i+1][j],mxy[i][j+1]);
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    fo(i,1,n)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        bo[a[i].x][a[i].y]=1;
    }   
    fo(i,0,2500)
    fo(j,0,2500)mnx[i][j]=mny[i][j]=inf;
    pre();
    memset(ur,-1,sizeof(ur));
    memset(dl,-1,sizeof(dl));
    fo(i,1,n)
    printf("%d\n",n+calur(a[i].x,a[i].y)+caldl(a[i].x,a[i].y)-3);
} 

你可能感兴趣的:(bzoj,DP,神奇脑洞题)