AT2542 誘拐 2 (Abduction 2)
一个很妙的题
思路
1. 暴力
我们直接从每个起点向各个方向出发,暴力求出能跑到的最大距离。复杂度为 \(2^{\max\{n, m\}}\)
2. 优化
显然,我们可以发现,我们直走到一个车流量比当前的路要大的路,才需要进行转弯。因此,我们可以对横路和纵路分别建立一棵线段树,里面存的是 \([l,r]\) 的路中最大的流量,通过类似于线段树二分的方法就可以找到最近的一条可转弯的路(当然,通过 ST表 进行倍增也可以做到)
3. 正解
但如果我们只优化直走的道路,这样的优化也并不大
我们又注意到,很多点是会重复经过的,因此我们可以用记忆化搜索,设 \(f_{k,i,j}\) ,\(k=0/1\) 表示南北或东西,\((i,j)\) 表示当前的坐标
我们不能开到 \(50000\times 50000\times 2\),但其实这个数组并不会用满,我们可以用 map 来优化空间
时间复杂度就是 \(O((n+m)q)\)
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, m, q, a[50005], b[50005];
struct Seg_Tree
{
    int tr[200005];
    #define ls (now << 1)
    #define rs ((now << 1) | 1)
    void build(int now, int l, int r, int a[])
    {
        if(l == r) {tr[now] = a[l]; return;}
        int mid = (l + r) >> 1;
        build(ls, l, mid, a), build(rs, mid + 1, r, a);
        tr[now] = std::max(tr[ls], tr[rs]);
    }
    int ql(int now, int l, int r, int p, int val)
    {
        if(p <= l || tr[now] <= val) return 0;
        if(l == r) return l;
        int mid = (l + r) >> 1;
        int re = ql(rs, mid + 1, r, p, val);
        if(re) return re;
        return ql(ls, l, mid, p, val);
    }
    int qr(int now, int l, int r, int p, int val)
    {
        if(r <= p || tr[now] <= val) return 0;
        if(l == r) return l;
        int mid = (l + r) >> 1;
        int re = qr(ls, l, mid, p, val);
        if(re) return re;
        return qr(rs, mid + 1, r, p, val);
    }
    #undef ls
    #undef rs
}r, c;
std::map<int, LL> f[2][50005];
LL solve(int x, int y, int ty) // 0 表示南北,1 表示东西
{
    if(f[ty][x][y]) return f[ty][x][y];
    LL re = 0;
    if(!ty)
    {
        int U = r.ql(1, 1, n, x, b[y]), D = r.qr(1, 1, n, x, b[y]);
        if(U) re = std::max(re, 1ll * (x - U) + solve(U, y, 1));
        else re = std::max(re, 1ll * (x - 1));
        if(D) re = std::max(re, 1ll * (D - x) + solve(D, y, 1));
        else re = std::max(re, 1ll * (n - x));
    }
    else
    {
        int L = c.ql(1, 1, m, y, a[x]), R = c.qr(1, 1, m, y, a[x]);
        if(L) re = std::max(re, 1ll * (y - L) + solve(x, L, 0));
        else re = std::max(re, 1ll * (y - 1));
        if(R) re = std::max(re, 1ll * (R - y) + solve(x, R, 0));
        else re = std::max(re, 1ll * (m - y));
    }
    return f[ty][x][y] = re;
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads(), m = reads(), q = reads();
    FOR(i, 1, n) a[i] = reads();
    FOR(i, 1, m) b[i] = reads();
    r.build(1, 1, n, a), c.build(1, 1, m, b);
    while(q--)
    {
        int x = reads(), y = reads();
        printf("%lld\n", std::max(solve(x, y, 0), solve(x, y, 1)));
    }
    return 0;
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号