bzoj4456: [Zjoi2016]旅行者

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

前言

话说我不是很会做这种类型的题TAT,还要多多练习

题解

分治+dij
首先如果我们现在的询问是这样的
这里写图片描述
红色是询问点
我们不妨可以吧把x,y差值较大的一个最为一条中轴线
在这里肯定就是那y啦,大概就是下面红点所在的先
然后询问就只有两种情况
1.如果在中轴线两端,肯定要越过中轴线
2.如果都在同一段,那么就可能越过也可能不越过

不越过的情况分治就行,我们讨论越过的情况

我们就枚举中轴线上的每一个先,跑当前分治范围的dij
然后更新答案
因为你要是穿过中轴线再回来,最短路肯定会与中轴线有交点,这个交点就是我们要决策的地方
时间复杂度O(跑得过) O(nmlog2(nm)nm)

我大概是这么算的
总共有log(n*m)层
然后每一层的分治有 nm 种取值
dij是 nmlog(nm)
于是就这样了。。
似乎算的不大对。。那就O(跑得过)吧
具体看代码吧

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define PI pair<int,int>
const int N=20005;
const int MAX=1<<30;
struct qq
{
    int x,y,z,last;
}s[N*4];int num,last[N];
int n,m,Q;
void init (int x,int y,int z)
{
    num++;
    s[num].x=x;s[num].y=y;s[num].z=z;
    s[num].last=last[x];
    last[x]=num;
}
int P (int x,int y){return (x-1)*m+y;}
struct qt
{
    int x1,y1,x2,y2;
    int id;
}ask[N*10],b[N*10];
int ans[N*10];
int d[N];
int ok[N],cnt;//这个点能不能用 
priority_queue<PI,vector<PI >,greater<PI > > q;
void dij (int now,int x1,int x2,int y1,int y2)
{
    cnt++;
    q.push(make_pair(0,now));
    for (int u=x1;u<=x2;u++)
        for (int i=y1;i<=y2;i++)
        {
            ok[P(u,i)]=cnt;
            d[P(u,i)]=MAX;
        }
    d[now]=0;
    while (!q.empty())
    {
        int x=q.top().second,lalal=q.top().first;
        q.pop();
        if (lalal>d[x]) continue; ;
        for (int u=last[x];u!=-1;u=s[u].last)
        {
            int y=s[u].y;
            if (ok[y]!=cnt) continue;//不在范围内
            if (lalal+s[u].z<d[y])
            {
                d[y]=lalal+s[u].z;
                q.push(make_pair(d[y],y));
             } 
        }
    }
}

void solve (int x1,int x2,int y1,int y2,int l,int r)
{
    if (x1>x2) return;
    if (y1>y2) return;
    if (l>r) return ;
    if (x2-x1>y2-y1)
    {
        int mid=(x2+x1)>>1;
        for (int u=y1;u<=y2;u++)
        {
            dij(P(mid,u),x1,x2,y1,y2);
            for (int i=l;i<=r;i++)
                ans[ask[i].id]=min(ans[ask[i].id],d[P(ask[i].x1,ask[i].y1)]+d[P(ask[i].x2,ask[i].y2)]);
        }
        int j=l-1,jj=r+1;
        for (int u=l;u<=r;u++)
            if (ask[u].x1<mid&&ask[u].x2<mid) b[++j]=ask[u];
       else if (ask[u].x1>mid&&ask[u].x2>mid) b[--jj]=ask[u];
        for (int u=l;u<=j;u++)  ask[u]=b[u];solve(x1,mid-1,y1,y2,l,j);
        for (int u=jj;u<=r;u++) ask[u]=b[u];solve(mid+1,x2,y1,y2,jj,r);
    }
    else
    {
        int mid=(y2+y1)>>1;
        for (int u=x1;u<=x2;u++)
        {
            dij(P(u,mid),x1,x2,y1,y2);
            for (int i=l;i<=r;i++)
                ans[ask[i].id]=min(ans[ask[i].id],d[P(ask[i].x1,ask[i].y1)]+d[P(ask[i].x2,ask[i].y2)]);
        }
        int j=l-1,jj=r+1;
        for (int u=l;u<=r;u++)
            if (ask[u].y1<mid&&ask[u].y2<mid) b[++j]=ask[u];
       else if (ask[u].y1>mid&&ask[u].y2>mid) b[--jj]=ask[u];
        for (int u=l;u<=j;u++)  ask[u]=b[u];solve(x1,x2,y1,mid-1,l,j);
        for (int u=jj;u<=r;u++) ask[u]=b[u];solve(x1,x2,mid+1,y2,jj,r);
    }

}
int main()
{
    memset(ans,127,sizeof(ans));
    memset(ok,0,sizeof(ok));cnt=0;
    num=0;memset(last,-1,sizeof(last));
    scanf("%d%d",&n,&m);
    for (int u=1;u<=n;u++)
        for (int i=1;i<m;i++)
        {
            int x;
            scanf("%d",&x);
            init(P(u,i),P(u,i+1),x);
            init(P(u,i+1),P(u,i),x);
        }
    for (int u=1;u<n;u++)
        for (int i=1;i<=m;i++)
        {
            int x;
            scanf("%d",&x);
            init(P(u,i),P(u+1,i),x);
            init(P(u+1,i),P(u,i),x);
        }
    scanf("%d",&Q);
    for (int u=1;u<=Q;u++)
    {
        scanf("%d%d%d%d",&ask[u].x1,&ask[u].y1,&ask[u].x2,&ask[u].y2);
        ask[u].id=u;
    }
    solve(1,n,1,m,1,Q);
    for (int u=1;u<=Q;u++) printf("%d\n",ans[u]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值