0w1

CFR 740 D. Alyona and a tree ( Monotonicity )

Problem - D - Codeforces

題意:
給一棵樹,有點權和邊權。定義 f( v, u ) 為真,若 u 為 v 的子樹裡的節點,且 dis( u, v ) ≤ 邊權[ u ]。對樹上所有節點 x,求 SIGMA( f( x, y ) | ALL y )。

資料規模:
節點數 1 ≤ n ≤ 2e5
點權 1 ≤ a[ i ]≤ 1e9
邊權 1 ≤ w[ i ]≤ 1e9
時限 2000 ms
記憶體 256 MB

解法:
先將 0 提為根,轉為有根樹。
由於 u 為 v 的子樹裡的節點,定義 d( x ) 為 x 至根的距離,那麼原不等式改寫成 d( u ) - d( v ) ≤ a( u ) -> d( u ) - a( u ) ≤ d( v )。因此每個 u 對每個祖先 v 都有一個貢獻,若且唯若該不等式滿足。如果按照 dfs 的順序處理,那只關心的是來自根的那條鏈。又注意到鏈越淺的位置的祖先 v,其 d( v ) 有越小的單調性,因此考慮類似子鏈前綴和的概念做貢獻的更新。方法就是預設當前的點會對所有祖先造成貢獻,貢獻會一直往上保留,但又因為可能某個祖先 v 以上的點都不能享受這些貢獻,便要在該祖先 v 的貢獻前綴和值 -1 補回來。

時間 / 空間複雜度:
O( N lg N ) / O( N )

int N;
vi A;
vvp G;

void init(){
    cin >> N;
    A = vi( N );
    for( int i = 0; i < N; ++i )
        cin >> A[ i ];
    G = vvp( N );
    for( int i = 1; i < N; ++i ){
        int P, W; cin >> P >> W;
        G[ P - 1 ].emplace_back( W, i );
    }
}

vi ans;

void dfs( int u, int fa, ll d, vector< pair< ll, int > > &chain ){ // dis( u, v ) ≤ a( u ) -> d( u ) - d( v ) ≤ a( u ) -> d( u ) - a( u ) ≤ d( v ) -> ++ans[ v ]
    ++ans[ u ];
    auto it = lower_bound( chain.begin(), chain.end(), make_pair( d - A[ u ], -1 ) );
    if( it-- != chain.begin() )
        --ans[ it->second ];
    chain.emplace_back( d, u );
    for( int i = 0; i < G[ u ].size(); ++i ){
        int w, v; tie( w, v ) = G[ u ][ i ];
        if( v == fa ) continue;
        dfs( v, u, d + w, chain );
        ans[ u ] += ans[ v ];
    }
    chain.pop_back();
}

void preprocess(){
    ans = vi( N );
    vector< pair< ll, int > > _chain; dfs( 0, -1, 0, _chain );
}

void solve(){
    for( int i = 0; i < N; ++i )
        cout << ans[ i ] - 1 << " \n"[ i + 1 == N ];
}