bzoj4456 [Zjoi2016]旅行者

4456: [Zjoi2016]旅行者

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 651  Solved: 375
[Submit][Status][Discuss]

Description

小Y来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有n条从东到西的道路和m条从南到北
的道路,这些道路两两相交形成n×m个路口 (i,j)(1≤i≤n,1≤j≤m)。她发现不同的道路路况不同,所以通过不
同的路口需要不同的时间。通过调查发现,从路口(i,j)到路口(i,j+1)需要时间 r(i,j),从路口(i,j)到路口(i+1
,j)需要时间c(i,j)。注意这里的道路是双向的。小Y有q个询问,她想知道从路口(x1,y1)到路口(x2,y2)最少需要
花多少时间。

Input

第一行包含 2 个正整数n,m,表示城市的大小。
 
接下来n行,每行包含m?1个整数,第i行第j个正整数表示从一个路口到另一个路口的时间r(i,j)。
 
接下来n?1行,每行包含m个整数,第i行第j个正整数表示从一个路口到另一个路口的时间c(i,j)。
 
接下来一行,包含1个正整数q,表示小Y的询问个数。
 
接下来q行,每行包含4个正整数 x1,y1,x2,y2,表示两个路口的位置。

Output

输出共q行,每行包含一个整数表示从一个路口到另一个路口最少需要花的时间。

Sample Input

2 2
2
3
6 4
2
1 1 2 2
1 2 2 1

Sample Output

6
7
n*m ≤ 2×10^4
q ≤ 10^5
相邻路口时间不超过10^4
分析:idea很妙的一道题.
   和棋盘迷宫这道题的想法差不多. 多组询问,要么一组一组地快速处理,要么像整体二分一样一下子全部处理完.  这道题采用平面内分治的方法来回答所有询问.
   画一条中线. 询问的两个点都在中线同一侧的询问,会在以后的递归中处理. 考虑两个点在中线异侧的询问. 这两个点之间的路径一定经过会经过中线上的两个点(这两个点可能重合).那么以中线上的每一个点为源点做dijkstra,用到达两个点的路径长度和去更新答案即可.
   那么中线是哪一条线呢?(到底是分治x还是分治y呢?)
   显然分治越大的越好,因为要枚举中线上的点. 如果x大,就分治x,y大就分治y.
   注意,这道题卡常数!几个小小的优化:
   1.dijkstra的vis数组用时间戳优化。
   2.如果dijkstra松弛操作的点在当前分治层的边界之外了,就不要去松弛它.
   3.数组开小点,memset就会快点.
   再来看一看这道题和棋盘迷宫有什么相似点:
   1.平面图(边与边的交点只会在顶点上,这样可以分治)
   2.多组询问,支持离线.
   3.询问的是路径.
   通用的解法是分治中线,求这条中线上的所有点到询问的点对之间的答案.
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 500010;
int n,m,ans[maxn],Q,d[20010],head[maxn],to[maxn],nextt[maxn],tot = 1,w[maxn],idx[maxn],idy[maxn],vis[maxn],Tim;

struct node
{
    int x3,y3,x4,y4,x,y,id;
} e[maxn],p[maxn];

struct node2
{
    int len,x;
    bool operator < (const node2 &b) const
    {
        return len > b.len;
    }
};

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void add(int x,int y,int z)
{
    w[tot] = z;
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

int id(int x,int y)
{
    return (x - 1) * m + y;
}

void dijkstra(int S,int x3,int y3,int x4,int y4)
{
    priority_queue <node2> q;
    Tim++;
    node2 temp;
    temp.x = S;
    temp.len = 0;
    q.push(temp);
    memset(d,127/3,sizeof(d));
    d[S] = 0;
    while (!q.empty())
    {
        node2 u = q.top();
        q.pop();
        if (vis[u.x] == Tim)
            continue;
        vis[u.x] = Tim;
        for (int i = head[u.x]; i; i = nextt[i])
        {
            int v = to[i];
            if (idx[v] < x3 || idx[v] > x4 || idy[v] < y3 || idy[v] > y4)
                continue;
            if (d[v] > d[u.x] + w[i])
            {
                d[v] = d[u.x] + w[i];
                node2 temp;
                temp.x = v;
                temp.len = d[v];
                q.push(temp);
            }
        }
    }
}

void solve(int x3,int y3,int x4,int y4,int ql,int qr)
{
    if (x3 > x4 || y3 > y4 || ql > qr)
        return;
    if (x4 - x3 <= y4 - y3)
    {
        int mid = (y3 + y4) >> 1;
        int L = ql - 1,R = qr + 1;
        for (int i = ql; i <= qr; i++)
        {
            if (e[i].y3 < mid && e[i].y4 < mid)
                p[++L] = e[i];
            else if (e[i].y3 > mid && e[i].y4 > mid)
                p[--R] = e[i];
        }
        for (int i = x3; i <= x4; i++)
        {
            dijkstra(id(i,mid),x3,y3,x4,y4);
            for (int j = ql; j <= qr; j++)
                ans[e[j].id] = min(ans[e[j].id],d[e[j].x] + d[e[j].y]);
        }
        for (int i = ql; i <= L; i++)
            e[i] = p[i];
        for (int i = R; i <= qr; i++)
            e[i] = p[i];
        solve(x3,y3,x4,mid - 1,ql,L);
        solve(x3,mid + 1,x4,y4,R,qr);
    }
    else
    {
        int mid = (x3 + x4) >> 1;
        int L = ql - 1,R = qr + 1;
        for (int i = ql; i <= qr; i++)
        {
            if (e[i].x3 < mid && e[i].x4 < mid)
                p[++L] = e[i];
            if (e[i].x3 > mid && e[i].x4 > mid)
                p[--R] = e[i];
        }
        for (int i = y3; i <= y4; i++)
        {
            dijkstra(id(mid,i),x3,y3,x4,y4);
            for (int j = ql; j <= qr; j++)
                ans[e[j].id] = min(ans[e[j].id],d[e[j].x] + d[e[j].y]);
        }
        for (int i = ql; i <= L; i++)
            e[i] = p[i];
        for (int i = R; i <= qr; i++)
            e[i] = p[i];
        solve(x3,y3,mid - 1,y4,ql,L);
        solve(mid + 1,y3,x4,y4,R,qr);
    }
}

int main()
{
    memset(ans,127/3,sizeof(ans));
    n = read(),m = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j < m; j++)
        {
            int t;
            t = read();
            add(id(i,j),id(i,j + 1),t);
            add(id(i,j + 1),id(i,j),t);
        }
    for (int i = 1; i < n; i++)
        for (int j = 1; j <= m; j++)
        {
            int t;
            t = read();
            add(id(i,j),id(i + 1,j),t);
            add(id(i + 1,j),id(i,j),t);
        }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            int t = id(i,j);
            idx[t] = i;
            idy[t] = j;
        }
    Q = read();
    for (int i = 1; i <= Q; i++)
    {
        e[i].x3 = read(),e[i].y3 = read(),e[i].x4 = read(),e[i].y4 = read();
        e[i].x = id(e[i].x3,e[i].y3);
        e[i].y = id(e[i].x4,e[i].y4);
        e[i].id = i;
    }
    solve(1,1,n,m,1,Q);
    for (int i = 1; i <= Q; i++)
        printf("%d\n",ans[i]);

    return 0;
}

 

posted @ 2018-04-02 23:49 zbtrs 阅读(...) 评论(...) 编辑 收藏