題解/算法 {3017. 按距离统计房屋对数目 II}

題解/算法 {3017. 按距离统计房屋对数目 II}

@LINK: https://leetcode.cn/problems/count-the-number-of-houses-at-a-certain-distance-ii/description/;

我們枚舉右端點r 然後計算他左側的所有點 距離他的距離, 即(x,y)滿足max(x,y)==r的數對; 即遍歷左端點l 那麽(l,r) 如果他的最短路是x 那麽ANS[x] += 2;
. 關鍵是看路徑, r->...->l的路徑, 要麽是形如r,r-1,r-2,... 要麽是形如r->Y->X->l(經過X-Y邊); 即對於[0,...r-1]這些點 他要到達r 有2種可能的最短路 {經過/不經過}X-Y邊;
. . 思路跳躍點: [0...,r-1]裏 一定存在一個點t, 對於[t,t+1,...,t-1]這些點 都是不經過X-Y的 即直接r->r-1->..即可, 通過區間元素全部進行+=K 因爲這些路徑長度是依次遞增的;
. . 否則對於<t的這些點 他們都是通過r->...->Y->X->...->路徑, 此時的情況 再分2種情況:{>=X的, <X的}, 這樣做的目的 是讓路徑長度是依次遞增的, 即到了X后 往左走 路徑長度是依次遞增的, 往右走 路徑長度也是依次遞增的;
因此 核心就在於 找到這個分界點t, 他是通過二分來求的, 二分條件是(是否直接一步步走abs(r-t) <= 强行通過X-Y), 他確實是符合二分的, 即靠近r的點 是直接過去 否則對於<t的點 都是通過經過X-Y邊的;

注意, 我們需要實現區間元素全部進行+=K, 當然可以用嬾標記綫段樹 但他是logN會超時的, 直接使用差分數組即可;

class Solution {
public:
    vector<long long> countOfPairs(int N, int X, int Y) {
        if( X>Y){ swap(X,Y);}  --X,--Y;
        static vector<int> vv;
        vv.resize( N);  memset( vv.data(), 0, sizeof(int)*vv.size());
        auto Add = [&]( int _l, int _r){
            ASSERT_( 1<=_l && _l<=_r && _r<=N);
            vv[ _l-1] += 2;
            if( _r < N){ vv[_r] -= 2;}
        };
        FOR_( r, 0, N-1){
            int ll = 0, rr = r;
            while( ll < rr){
                auto mid = (ll+rr)/2;
                if( abs(r-mid) <= abs(r-Y)+1+abs(X-mid)){ rr = mid;}
                else{ ll = mid + 1;}
            }
            //> `[rr,rr+1,...,r]`
            if( abs(r-rr) > 0){ Add( 1, abs(r-rr));}

            //> `[X+1,...,rr-1]` (r->Y->X->?)
            auto cont = rr-1 - X;
            auto d = abs(r-Y) + 1;
            if( cont > 0){
                Add( d+1, d+cont);
            }

            //> `[0,...,min(X,rr-1)]` (r->Y->X->?)
            if( min(X,rr-1) >= 0){
                Add( d, d + min(X,rr-1));
            }
        }
        
        FOR_( i, 1, N-1){ vv[i] += vv[i-1];}
        vector<long long> ANS( N, 0);
        FOR_( i, 0, N-1){
            ANS[i] = vv[i];
        }
        return ANS;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值