1. 程式人生 > >HDU 4836 —— The Query on the Tree(線段樹+LCA)

HDU 4836 —— The Query on the Tree(線段樹+LCA)

下午百度之星複賽裡最簡單的一題,雖然我還是1個小時才AC的,呃,下午果斷被虐粗翔。

本題磨了1個小時才過,第1題還是很猥瑣地用了隨機數過的(真不知道怎麼做)。

回到這題來,其實也不知道大牛們怎麼做的,我只能用線段樹+LCA搞了。

首先考慮根不改變的情況,那麼我們可以將每個結點對映到線段樹上去,讓每個點對應的子樹的點都落在某個連續區間上;

具體是在dfs的時候,用id來表示當前已經對映到線段樹的編號,初始為0,進入某個結點X,令left[X]=right[X]=++id,然後繼續dfs,對於X每個子節點j,dfs完了順便更新right[X]=max(right[X], right[j]),這樣在所有子節點遍歷完的情況下,left[X]到right[X]就剛好是以X為根的子樹的區間。在紙上模擬下就明白了。

然後對於查詢和點更新就是個經典的線段樹問題了。

比較麻煩的是根被改變的情況,假設我們現在要查詢以X為根的子樹的和,分析下當前根是root時與原先的不同。如果root原來就是X的祖先了,那麼答案跟原來是一樣的,再往下想其實就是隻要root需要經過X原先某個祖先到達X的話,答案應該都一樣的,所以這個好辦。

而如果X剛好就是root,那麼答案就是整棵樹的和,也好處理。

剩下的就是原來X是root的祖先,我們假設X經過它的某個子節點Y到達root,那麼很明顯當前X的子樹和,應該是整棵樹的值減去原來Y對應的子樹的和。因為X-Y這條邊是root進行dfs到達X的最後一條邊,換句話說這條邊就把是不是X的子樹的點分隔開了。

分析到這裡問題就好辦了,對於查詢X,求出它們的最近公共祖先V,再進行判斷即可。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define lson o<<1
#define rson (o<<1)|1
#define N 10001
#define pb push_back
vector<int> V[N];
int t, ct, n;
int left[N], right[N], val[N], v2[N];
int s[N<<2], l[N<<2], r[N<<2];
int id, root;
bool f[N];
int depth[N], parent[16][N];
void dfs(int x){
    left[x] = right[x] = ++id;
    v2[id] = val[x];
    for(int i=0; i<V[x].size(); i++){
        int j=V[x][i];
        if(f[j])    continue;
        f[j]=1;
        depth[j] = depth[x]+1;
        parent[0][j] = x;
        dfs(j);
        right[x] = max(right[x], right[j]);
    }
}
void maintain(int o){
    s[o] = s[lson]+s[rson];
}
void build(int o, int ll, int rr){
    l[o]=ll; r[o]=rr;
    s[o]=0;
    if(ll<rr){
        int m = (ll+rr)>>1;
        build(lson, ll, m);
        build(rson, m+1, rr);
        maintain(o);
    }
    else{
        s[o] = v2[ll];
    }
}
void update(int o, int p, int v){
    if(l[o]==p && r[o]==p){
        s[o]=v;
        return;
    }
    int m = (l[o]+r[o])>>1;
    if(p<=m)    update(lson, p, v);
    else    update(rson, p, v);
    maintain(o);
}
void init_lca(){
    for(int k=0; k<15; k++){
        for(int v=1; v<=n; v++){
            if(parent[k][v]<0)  parent[k+1][v]=-1;
            else    parent[k+1][v] = parent[k][parent[k][v]];
        }
    }
}
int lca(int u, int v){
    if(depth[u]>depth[v]){
        swap(u,v);
    }
    for(int k=0; k<16; k++){
        if((depth[v]-depth[u])>>k & 1){
            v = parent[k][v];
        }
    }
    if(u==v)    return u;
    for(int k=15; k>=0; k--){
        if(parent[k][u]!=parent[k][v]){
            u=parent[k][u];
            v=parent[k][v];
        }
    }
    return parent[0][u];
}
int query(int o, int ll, int rr){
    if(l[o]==ll && r[o]==rr)    return s[o];
    int m = (l[o]+r[o])>>1;
    if(rr<=m)   return query(lson, ll, rr);
    else if(ll>m)   return query(rson, ll, rr);
    else    return query(lson, ll, m)+query(rson, m+1, rr);
}
int query(int x){
    if(x==root) return s[1];
    int v = lca(root, x);
    if(v!=x){
        return query(1, left[x], right[x]);
    }
    for(int i=0; i<V[x].size(); i++){
        int j=V[x][i];
        if(depth[j]<depth[x])   continue;
        v = lca(root, j);
        if(v==j){//找到前面所說的Y
            return s[1] - query(1, left[j], right[j]);
        }
    }
    return 0;
}
inline void in(int &x){
    char c=getchar();
    x=0;
    while(c<48 || c>57) c=getchar();
    while(c>=48 && c<=57){
        x = x*10+c-48;
        c = getchar();
    }
}
int main(){
    in(t);
    for(ct=1; ct<=t; ct++){
        printf("Case #%d:\n", ct);
        in(n);
        for(int i=1; i<=n; i++) V[i].clear();
        int x, y;
        for(int i=1; i<n; i++){
            in(x); in(y);
            V[x].pb(y);
            V[y].pb(x);
        }
        for(int i=1; i<=n; i++){
            in(val[i]);
        }
        memset(f,0,sizeof(f));
        memset(parent,-1,sizeof(parent));
        f[1]=1;
        id = 0;
        depth[1]=1;
        dfs(1);
        init_lca();
        build(1, 1, n);
        int q;
        char op[10];
        in(q);
        root=1;
        while(q--){
            scanf("%s", op);
            in(x);
            if(op[0]=='Q'){
                printf("%d\n", query(x));
            }
            else if(op[0]=='C'){
                in(y);
                update(1, left[x], y);
            }
            else{
                root = x;
            }
        }
    }
    return 0;
}


相關推薦

hdu-4836-The Query on the Tree線段+LCA

題目連結 思路:對於每次詢問,主要是看x和root的關係,求出root和xlca root=x ,ans為總的和 lca=x  那麼ans=總的和-(root到x這條鏈上父節點為x的那個點的子樹和) 否則,ans就是x的子樹和 求子樹和和修改直接線段樹維護。節點的編

HDU 4836 —— The Query on the Tree線段+LCA

下午百度之星複賽裡最簡單的一題,雖然我還是1個小時才AC的,呃,下午果斷被虐粗翔。 本題磨了1個小時才過,第1題還是很猥瑣地用了隨機數過的(真不知道怎麼做)。 回到這題來,其實也不知道大牛們怎麼做的,我只能用線段樹+LCA搞了。 首先考慮根不改變的情況,那麼我們可以將每個

SPOJ 375 Query on a tree初學鏈剖分

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions

HDU 4027 Can you answer these queries? 線段+暴力

題意: 給出一段序列和兩種操作,第一種操作,將x,y區間的數均開平方,第二種操作,對x,y區間進行求和。 分析: 一開始還不敢用線段樹做,因為純線段樹下來根暴力列舉複雜度差不了多少,但由於開方,所以在很少次的迴圈裡就能達到1,所以就可以直接這麼做了。但題目沒有說名忍耐值的範圍,要是0太多

HDU4836 The Query on the Tree dfs+線段

題目分析:首先如果不換根的話,就可以用dfs求時間戳+樹狀陣列維護即可。 現在多了換根操作,我們該怎麼處理? 首先因為換根並不會改變樹的結構,所以我們依舊dfs出一棵樹來。 對於修改,我們改變該點在樹狀陣列上對應位置的值即可。 對於換根,我們直接將root置為要換的節

hdu 6191--Query on A Tree持久化字典

out trie scribe nodes include mathjax osi lan push_back 題目鏈接 Problem Description Monkey A lives on a tree, he always plays on this t

2017廣西邀請賽 Query on A Tree 可持續化字典

題意 second for each follow n) nod pair content back Query on A Tree 時間限制: 8 Sec 內存限制: 512 MB提交: 15 解決: 3[提交][狀態][討論版] 題目描述 Monkey

hdu 5381 The sum of gcd線段+gcd

const HR LV oid pac vector AR statistic modify 題目鏈接:hdu 5381 The sum of gcd 將查詢離線

HDU 6191】Query on A Tree 【可持久化字典

algorithm stream %d ons turn class img cstring str 題目 給出一棵有n個結點的樹,樹根是1,每個結點給出一個value。然後給出q個詢問,每個詢問給出兩個整數u和x,你要在以u結點為根的子樹中找出一個結點v,使得val

SPOJ375——Query on a tree鏈剖分模板詳解以及入門

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfro

BZOJ_1803_Spoj1487 Query on a tree III_主席+dfs序

while out 主席樹 contains RR light oid contain space BZOJ_1803_Spoj1487 Query on a tree III_主席樹 Description You are given a node-labeled r

HDU-6035:Colorful Tree+DP

node different ase 得到 第一題 false all 直接 files 這裏有三道長得像的題: 一: HDU6036: There is a tree with nn nodes, eac

SPOJ COT Count on a tree主席+倍增lca

等於 lca amp oot namespace wap 1+n tor n) 思路:這個題其實就是樹上的第k小,主席樹的本質還是類似於前綴和一樣的結構,所以是完全相同的,所以我們在樹上也可以用同樣的方法,我們對於每一個節點進行建樹,然後和普通的樹上相同,ab之間的距離是等

Who Gets the Most Candies?線段+模擬

【題意】 NNN 個孩子順時針坐成一個圓圈(編號從 111 到 NNN ),每個孩子手中有一張卡片(上有一個非0整數)現在讓第K個孩子先淘汰,如果該孩子手中卡片上的數字A大於零,下一個出圈的是他左手邊第A個孩子,否則,下一個出圈的是他右手邊第A個孩子(當前孩子

Count the Colors線段染色

Count the Colors Time Limit:2000MS    Memory Limit:65536KB    64bit IO Format:%lld & %llu Description Painting some colored segme

POJ 題目2985 The k-th Largest Group線段單點更新求第k大值,並查集

The k-th Largest Group Time Limit: 2000MS Memory Limit: 131072K Total Submissions: 7869 Accepted: 2534 Description Newman likes play

HDU 4027 Can you answer these queries?線段區間開方

sizeof sqrt .cn swap %d nes nts following clr Can you answer these queries? Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 6576

HDU 3340 Rain in ACStar線段+幾何

itl microsoft push php right not this string rain HDU 3340 Rain in ACStar pid=3340" target="_blank" style="">題目鏈接 題意:給定幾個多邊形(

POJ 題目3321 Apple Tree線段

nes num ons source 每一個 number autumn script ise Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submission

HDU 5172 GTY's gay friends 線段

ace tac 分享 log 沒有 http inf hdu algorithm 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5172 題意:   給你一個n個數的數組,m次詢問,詢問在[L, R] 這個區間裏面有沒有