題解/算法 {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;
}
};