codeforces 894D

(對二叉樹有感覺的話,思路還是出得蠻快的)
題意:給定一個n 節點二叉樹(n<106 ),每條邊上有一個權值,然後給出mm<105 )個詢問,求Ai 節點在Hi 距離內能到達的樹上哪些節點,求到達這些節點經過的距離之和。

思路:如果每次處理詢問都要對樹上節點遍歷的話,肯定超時,於是想到預處理,由於這個樹是很標準的二叉樹,那麼我們可以試試對於每個樹節點,求出每個子節點距離它的值,並保存起來(就像線段樹中的push_up操作一樣)。然後對於每一個詢問Ai ,我們可以利用二分查找迅速求出這個節點所能到達它下面子樹節點的距離和,然後想到達剩下的節點無非要經過Ai/2 或者Ai1 這兩個節點。然後我們也可以迅速得到Ai1 下面子樹的結果。對於Ai/2 上面的點,我們依次遍歷這些店直到樹根,然後每次處理這些點對應的Ai1 子樹即可。

時間複雜度:預處理+查詢:O(nlog(n)+mlog(n)2)
空間複雜度:預處理的結果:O(nlog(n))

(開始以爲可能會MLE,然後發現題目中內存比較大。好久沒寫代碼,寫的時候調了半天(居然以爲break語句能跳出它上面的if-else語句…)。)

#include <cstdio>
#include <algorithm>
#include <vector>
#define LL long long

using namespace std;
const int maxn = 1000050;
const int inf = 0x3f3f3f3f;

int len[maxn][2];
vector<int> vec[maxn];
vector<LL> sum[maxn];

void push_up(int p, int n) {
    // merge sort
    int l = p<<1, r = p<<1|1, i = 0, j = 0;
    int l_len = len[p][0], r_len = r<=n? len[p][1]:0;
    while(i < (int)vec[l].size()-1 && j < (int)vec[r].size()-1) {
        if(vec[l][i]+l_len < vec[r][j]+r_len)
            vec[p].push_back(vec[l][i++]+l_len);
        else
            vec[p].push_back(vec[r][j++]+r_len);
        if(vec[p].back() >= inf) break ;
    }
    while(i < (int)vec[l].size()-1 && vec[p].back() < inf)
        vec[p].push_back(vec[l][i++]+l_len);
    while(j < (int)vec[r].size()-1 && vec[p].back() < inf)
        vec[p].push_back(vec[r][j++]+r_len);
    if(vec[p].back() >= inf) vec[p].pop_back();
    // calculate prefix sum
    sum[p].push_back(vec[p][0]);
    for(int k=1; k<(int)vec[p].size(); k++) {
        sum[p].push_back(vec[p][k]);
        sum[p][k] += sum[p][k-1];
    }
    return ;
}

void build(int p, int n) {
    int lson = p<<1, rson = p<<1|1;
    if(lson <= n) build(lson, n);
    if(rson <= n) build(rson, n);
    vec[p].clear();
    vec[p].push_back(0);
    if(lson <= n || rson <= n)
        push_up(p, n);
    vec[p].push_back(inf);
    return ;
}

LL get_sum(int n, int id, int h) {
    LL ret = h;
    int pos = upper_bound(vec[id].begin(), vec[id].end(), h) - vec[id].begin() - 1;
    if(pos >= 1) ret += (LL)h*pos - sum[id][pos];
    //printf("ret1 : %I64d\n",ret);
    while(id != 1) {
        h -= len[id/2][id&1];
        if(h > 0) ret += h;
        else break ;
        //printf("ret2 : %I64d\n",ret);
        int id_2 = id ^ 1, branch = len[id/2][id_2&1];
        if(id_2 <= n && h-branch > 0) {
            ret += h-branch;
            int pos = upper_bound(vec[id_2].begin(), vec[id_2].end(), h-branch) - vec[id_2].begin() - 1;
            if(pos >= 1) ret += (LL)(h-branch)*pos - sum[id_2][pos];
            //printf("ret3 : %I64d\n",ret);
        }
        id /= 2;
    }
    return ret;
}

int main() {
    int n, m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<n; i++) {
        int t, st = (i+1)/2;
        scanf("%d",&t);
        len[st][(i+1)&1] = t;
    }
    build(1, n);
    while(m --) {
        int id, h;
        scanf("%d%d",&id,&h);
        LL ans = get_sum(n, id, h);
        printf("%I64d\n",ans);
    }
    return 0;
}
發佈了39 篇原創文章 · 獲贊 44 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章