zoukankan      html  css  js  c++  java
  • hdu6800

    航电多校第三场

      这道题的思维很是巧妙啊,虽然赛时大概有了想法,但是当初没学会树套树,还是过不了的啊,感觉题解有点太难懂了,再一篇博客,分享一下自己标注过的std;

      首先是这题一共只有两只手,而且按顺序跑n个点,也就是说,假如dp[i][j]代表着你左手在i,右手在j的时候的最优解,其实dp[i][j]=dp[j][i];所以说当最大的点为i时,你只需要关心另外一只手放在哪个点了就行了,也就是说dp[i+1][j]只和dp[i][*]有关系,和dp[i-1][*]只有间接关系。

      然后为了dp好弄一点,dp存的值不是当前答案时多少,而是我们通过交换手省下了多少的步长。

      首先除了dp[i][i-1]以外,dp[i][j]都是等于dp[i-1][j]的,为什么呢,因为从dp[i-1][j]到dp[i][j],我们不会换手,所以说我们不会省下任何步长,所以我们每跑一个点,只需要更新dp[i][i-1]即可。

      然后dp[i][i-1]怎么更新呢,就变成了二维平面上,找一个j点与这个点的曼哈顿距离加上dp[i][j]最小,这个可以通过树套树loglog时间解决

    #include <iostream>
    #include <string.h>
    #include <queue>
    #include <stack>
    #include <map>
    #include <stdio.h>
    #include <algorithm>
    #include <math.h>
    #include <unordered_map>
    using namespace std;
    typedef long long LL;
    const int maxn = (int)1e5 + 1;
    
    namespace Memory {
        const int maxm = maxn * 18 + 1, maxl = maxm * 8 + 1;
        int pool[maxm], *pool_tail;
        LL info[maxl], *info_tail;
    
        inline void reset() {
            pool_tail = pool;
            info_tail = info;
        }
        inline int *ask32(int len) {
            int *ret = pool_tail;
            pool_tail += len;
            return ret;
        }
        inline LL *ask64(int len) {
            LL *ret = info_tail;
            info_tail += len;
            return ret;
        }
    }
    
    int n, qtot;
    LL dp[maxn];
    pair<int, int> seq[maxn], que[maxn];
    
    inline void upd_min(LL &x, LL y) {
        x > y && (x = y);
    }
    
    inline bool cmp_y(int const &u, int const &v) {
        return que[u].second < que[v].second;
    }
    
    struct Segment {
        int ytot, *yque;
        LL *info;
    } seg[maxn << 1 | 1];
    
    inline int seg_idx(int L, int R) {//线段树L-R的hash映射
        return (L + R) | (L < R);
    }
    
    
    void seg_init_inner(Segment &rt, int L, int R) {
        if(L == R)
            return;
        int M = (L + R) >> 1;
        seg_init_inner(rt, L, M);
        seg_init_inner(rt, M + 1, R);
        LL *cur = rt.info + (seg_idx(L, R) << 2);
        LL *lft = rt.info + (seg_idx(L, M) << 2);
        LL *rht = rt.info + (seg_idx(M + 1, R) << 2);
        cur[0] = min(lft[0], rht[0]);
        cur[1] = min(lft[1], rht[1]);
        cur[2] = min(lft[2], rht[2]);
        cur[3] = min(lft[3], rht[3]);
    }
    
    void seg_init_outer(int L, int R) {
        
        static int ord[maxn];
        
        if(L < R) {
            int M = (L + R) >> 1;
            seg_init_outer(L, M);
            seg_init_outer(M + 1, R);
            inplace_merge(ord + L, ord + M + 1, ord + R + 1, cmp_y);//按que[u].second 有序合并
        }
        else
        {
            ord[L] = L;
        }
        
        Segment &rt = seg[seg_idx(L, R)];
        
        rt.ytot = 1;
        for(int i = L + 1; i <= R; ++i)
            rt.ytot += cmp_y(ord[i - 1], ord[i]);//L到R中到底有多少个不同的y值
        
        rt.yque = Memory::ask32(rt.ytot);
        rt.info = Memory::ask64(((rt.ytot - 1) << 1 | 1) << 2); ((n-1)*2+1)*4;
        
        
        for(int i = L, j = 0; i <= R; ++j) {
            int u = ord[i++], y = que[u].second, xL = que[u].first, xR = xL;
            
            
            for(int v; i <= R && !cmp_y(u, v = ord[i]); ++i) { //如果u+1和u同高,继续u++
                int tmp = que[v].first;
                if(tmp < xL) {
                    xL = tmp;
                } else if(tmp > xR) {
                    xR = tmp;
                }
                //扩充当前的左右边界
            }
            //这里的j是按高度不同才增一次的
            rt.yque[j] = y;
            
            LL *val = rt.info + (seg_idx(j, j) << 2);//4*j
            val[0] = -xR - y;//各个象限对应的值
            val[1] = -xR + y;
            val[2] = xL - y;
            val[3] = xL + y;
        }
        //内层的线段树
        //内层的线段树
        seg_init_inner(rt, 0, rt.ytot - 1);
    }
    
    inline void seg_update_inner(Segment &rt, int y, LL val[]) {
        
        for(int L = 0, R = rt.ytot - 1; L <= R; ) {
            LL *cur = rt.info + (seg_idx(L, R) << 2);
            upd_min(cur[0], val[0]);
            upd_min(cur[1], val[1]);
            upd_min(cur[2], val[2]);
            upd_min(cur[3], val[3]);
            if(L == R)
                break;
            
            int M = (L + R) >> 1;
            
            if(y <= rt.yque[M]) {
                R = M;
            } else {
                L = M + 1;
            }
            
            
        }
        
    }
    
    inline void seg_update_outer(int pos) {
        int x = que[pos].first, y = que[pos].second;
        LL v = dp[pos], val[5] = {
            v - x - y,
            v - x + y,
            v + x - y,
            v + x + y
        };
        for(int L = 0, R = qtot - 1; L <= R; ) {
            
            seg_update_inner(seg[seg_idx(L, R)], y, val);
            
            if(L == R)
                break;
            
            int M = (L + R) >> 1;
            
            if(pos <= M) {
                
                R = M;
                
            } else {
                L = M + 1;
            }
        }
    }
    
    inline LL seg_query_left_inner(Segment &rt, int x, int y) {
        LL ret = LLONG_MAX;
        for(int L = 0, R = rt.ytot - 1; L <= R; ) {
            LL *cur = rt.info + (seg_idx(L, R) << 2);
            if(y <= rt.yque[L]) {
                upd_min(ret, cur[1] - y);
                break;
            } else if(y >= rt.yque[R]) {
                upd_min(ret, cur[0] + y);
                break;
            }
            
            int M = (L + R) >> 1;
            if(y <= rt.yque[M]) {
                LL *rht = rt.info + (seg_idx(M + 1, R) << 2);
                upd_min(ret, rht[1] - y);
                R = M;
            } else {
                LL *lft = rt.info + (seg_idx(L, M) << 2);
                upd_min(ret, lft[0] + y);
                L = M + 1;
            }
            
        }
        if(ret != LLONG_MAX)
            ret += x;
        return ret;
    }
    
    inline LL seg_query_right_inner(Segment &rt, int x, int y) {
        LL ret = LLONG_MAX;
        for(int L = 0, R = rt.ytot - 1; L <= R; ) {
            LL *cur = rt.info + (seg_idx(L, R) << 2);
            if(y <= rt.yque[L]) {
                upd_min(ret, cur[3] - y);
                break;
            } else if(y >= rt.yque[R]) {
                upd_min(ret, cur[2] + y);
                break;
            }//已经可以确定在哪个象限有答案了
            
            int M = (L + R) >> 1;
            
            if(y <= rt.yque[M]) {
                
                LL *rht = rt.info + (seg_idx(M + 1, R) << 2);
                //在右上方的点的贡献也可以算了
                upd_min(ret, rht[3] - y);
                R = M;//从新区间L-M再来
            } else {
                LL *lft = rt.info + (seg_idx(L, M) << 2);
                upd_min(ret, lft[2] + y);
                L = M + 1;
            }
        }
        if(ret != LLONG_MAX)
            ret -= x;
        return ret;
    }
    
    inline LL seg_query_outer(int pos) {
        int x = que[pos].first, y = que[pos].second;
        LL ret = dp[pos];
        for(int L = 0, R = qtot - 1; L < R; ) {
            int M = (L + R) >> 1;
            
            if(pos <= M) {
                upd_min(ret, seg_query_right_inner(seg[seg_idx(M + 1, R)], x, y));
                R = M;
            } else {
                upd_min(ret, seg_query_left_inner(seg[seg_idx(L, M)], x, y));
                L = M + 1;
            }
            
        }
        
        return ret;
    }
    
    void solve() {
        scanf("%d", &n);
        for(int i = 0; i < n; ++i) {
            scanf("%d%d", &seq[i].first, &seq[i].second);
            que[i] = seq[i];
            dp[i] = 0;
        }
        sort(que, que + n);
        qtot = unique(que, que + n) - que;
    
        Memory::reset();
        seg_init_outer(0, qtot - 1);//线段树初始化
        
    
        LL ans = 0, sum = 0;
            
        int las = lower_bound(que, que + qtot, seq[0]) - que;
        
        for(int i = 1; i < n; ++i) {
            LL adt = abs(seq[i].first - seq[i - 1].first) + abs(seq[i].second - seq[i - 1].second);
            int pos = lower_bound(que, que + qtot, seq[i]) - que;
            LL best = seg_query_outer(pos) - adt;//以换手的代价跑到pos,省了多少的代价
            
            if(dp[las] > best) {//比已经省下的多
                
                ans=min(ans , dp[las] = best );
                
                seg_update_outer(las);
            }
            
            sum += adt;
            las = pos;
        }
        printf("%lld
    ", ans + sum);
    }
    int main() {
        int T;
        scanf("%d", &T);
        for(int Case = 1; Case <= T; ++Case) {
            // printf("Case #%d:
    ", Case);
            solve();
        }
        return 0;
    }
  • 相关阅读:
    js全选 反选 不选 代码示例。
    前端超级好用a标签跳转带锚点效果
    10分钟倒计时简易
    点赞投票+1简单jq代码
    登录注册密码可见与不可见jquery简易效果开发
    JS调用函数内部变量有以下两种方法:
    关于clipboard.js复制图片以及文本的随笔
    这是我的第一个博客,我叫小白菜!
    前端之路(一)之W3C是什么?
    知识普及
  • 原文地址:https://www.cnblogs.com/King-of-Dark/p/13419048.html
Copyright © 2011-2022 走看看