1. 程式人生 > >[BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #線段樹合並

[BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #線段樹合並

sub click 情況 pac include pri 使用 oid source

4756: [Usaco2017 Jan]Promotion Counting

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 298 Solved: 210
[Submit][Status][Discuss]

Description

The cows have once again tried to form a startup company, failing to remember from past experience t hat cows make terrible managers!The cows, conveniently numbered 1…N1…N (1≤N≤100,000), organize t he company as a tree, with cow 1 as the president (the root of the tree). Each cow except the presid ent has a single manager (its "parent" in the tree). Each cow ii has a distinct proficiency rating, p(i), which describes how good she is at her job. If cow ii is an ancestor (e.g., a manager of a man ager of a manager) of cow jj, then we say jj is a subordinate of ii. Unfortunately, the cows find that it is often the case that a manager has less proficiency than seve ral of her subordinates, in which case the manager should consider promoting some of her subordinate s. Your task is to help the cows figure out when this is happening. For each cow ii in the company, please count the number of subordinates jj where p(j)>p(i). n只奶牛構成了一個樹形的公司,每個奶牛有一個能力值pi,1號奶牛為樹根。
問對於每個奶牛來說,它的子樹中有幾個能力值比它大的。

Input

The first line of input contains N The next N lines of input contain the proficiency ratings p(1)…p(N) for the cows. Each is a distinct integer in the range 1…1,000,000,000 The next N-1 lines describe the manager (parent) for cows 2…N Recall that cow 1 has no manager, being the president. n,表示有幾只奶牛 n<=100000
接下來n行為1-n號奶牛的能力值pi
接下來n-1行為2-n號奶牛的經理(樹中的父親)

Output

Please print N lines of output. The ith line of output should tell the number of subordinates of cow ii with higher proficiency than cow i. 共n行,每行輸出奶牛i的下屬中有幾個能力值比i大

Sample Input

5
804289384
846930887
681692778
714636916
957747794
1
1
2
3

Sample Output

2
0
1
0
0

HINT

Source

Platinum鳴謝Acty提供譯文

Analysis

學習到了一個神級操作 0_o :::::::::::::::::::::::::::::::::::: 線段樹合並!

首先我們要給線段樹改成:動態開點+散結構+不完整樹

我們對一個序列建一棵經典線段樹,那麽他是長什麽樣的我們大致都是知道的

但如果要想並查集一樣分裂合並的話(分裂還沒學呀qwq),首先這棵樹得是不完整的

如圖,這是一棵經典線段樹

技術分享

那麽如果我們對每一個元素建一棵線段樹(這樣他們最終可以合並成一棵)的話

看起來是這樣

技術分享

上面是兩個單個結點的線段樹

他們合並後是

技術分享

那麽對於每一層結點,他們只保存這棵線段樹內已有的元素的信息

但是就建制而言(就是每一層結點保存的範圍),他們是與相同情況下的經典線段樹一樣的

那麽這就顯然:原來那套 Lchild = root*2 的方式已經不適用了,應當使用動態開點的方式

關鍵點一就在此了

題意:查找每一個頂點的子樹內數值比該頂點大的點的數量

那麽建立一棵權值可合並線段樹

權值線段樹:指以值為下標的線段樹,在經典線段樹裏需要離散化,但可合並線段樹使其實現更加容易

如果是權值線段樹的話,那麽我們的查詢區間就不是元素下標了,而是元素的值域

很棒啊!

具體內容還是需要理解代碼,無論是權值線段樹還是可合並線段樹都沒什麽太多好講的

Code

技術分享
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define maxn 1000000
using namespace std;

int arr[maxn],disc[maxn],n,ans[maxn];

vector<int> G[maxn];

struct node{ int lc,rc,L,R,sum; };

//namespace SegmentTree{
    int root[maxn] = {0},tot = 0;
    node T[maxn*5];
    
    int maintain(int rt){ T[rt].sum = T[T[rt].lc].sum+T[T[rt].rc].sum; }
    
    int build(int L,int R,int pos){
        int rt = ++tot,mid = (L+R)/2;
        T[rt].L = L, T[rt].R = R, T[rt].sum = 0;
        if(L == R) return T[rt].sum = 1,rt;
        if(pos <= mid) T[rt].lc = build(L,mid,pos);
        else T[rt].rc = build(mid+1,R,pos);
        return maintain(rt),rt;
    }
    
    int query(int rt,int qL,int qR){
        if(!rt) return 0;
        if(qL <= T[rt].L && T[rt].R <= qR) return T[rt].sum;
        int ret = 0,mid = (T[rt].L+T[rt].R)>>1;
        if(qL <= mid) ret += query(T[rt].lc,qL,qR);
        if(qR > mid) ret += query(T[rt].rc,qL,qR);
        return ret;
    }
    
    int merge(int x,int y){
        if(!x||!y) return x^y;
        T[x].lc = merge(T[x].lc,T[y].lc);
        T[x].rc = merge(T[x].rc,T[y].rc);
        return maintain(x),x;
    }
    
    void dfs(int u,int fa){
        int res = 0;
        for(int i = 0;i < G[u].size();i++){
            if(G[u][i] == fa) continue;
            int v = G[u][i];
            dfs(v,u);
            res += query(root[v],arr[u]+1,n);
            root[v] = merge(root[u],root[v]);
        }ans[u] = res;
    }
//}

int main(){
    scanf("%d",&n);
    
    for(int i = 1;i <= n;i++) scanf("%d",&arr[i]),disc[i] = arr[i];
    sort(disc+1,disc+1+n);
    int m = unique(disc+1,disc+1+n)-disc-1;
    for(int i = 1;i <= n;i++) arr[i] = lower_bound(disc+1,disc+1+m,arr[i])-disc;
    
    for(int i = 2;i <= n;i++){
        int fa; scanf("%d",&fa);
        G[i].push_back(fa);
        G[fa].push_back(i);
    }
    
    for(int i = 1;i <= n;i++) root[i] = build(1,n,arr[i]);
    
    dfs(1,1);
    
    for(int i = 1;i <= n;i++) printf("%d\n",ans[i]);
    
    return 0;
}
線段樹合並+權值線段樹

[BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #線段樹合並