每日一題 3月31日 城市網絡 樹上倍增 or DFS+線段樹 or DFS+單調棧

題目鏈接:https://ac.nowcoder.com/acm/problem/13331
在這裏插入圖片描述
在這裏插入圖片描述

//我們講一講這個是怎麼逼近
//可以證明f[u][0]一定是f[father[u]][i]購買路徑上的節點
//把一個查詢作爲一個節點。那麼最後就是看從查詢的u節點開始能最多跳多少次,並且不超過v節點。
//往上倍增尋找第一個比a[u]大的節點下面那個節點(他的父親就是第一個比a[u]大的節點);
 
int x=fa;
for(int i=20; i>=0; i--){
    //找到<=a[u]的第一個節點。那麼這個節點肯定處於f[x][i]~f[x][i-1]
    //從f[x][i-1]開始區間長度爲i-1。繼續逼近
    if(f[x][i]&&a[u]>=a[f[x][i]]){
        x=f[x][i];
    }
}
f[u][0]=f[x][0];//f[x][0]就是第一個比a[u]大的節點

在這裏插入圖片描述
這裏是可以撤銷的單調棧,在一個節點只會變化兩個:tot和一個棧內的元素,保存並且恢復就可以了

//思路一:
#include <bits/stdc++.h>
using namespace std;
 
int a[200005], R[200005];
vector<vector<int> > v(200005);
int f[200005][25], deep[200005];
void dfs(int u, int fa){
    deep[u]=deep[fa]+1;
    if(a[u]<a[fa]){//若父節點p的val比u的val大,fa[u][0] = p
        f[u][0]=fa;
    }
    else{
        int x=fa;
        for(int i=20; i>=0; i--){//x逼近第一個比a[u]大的結點下方的結點
            if(f[x][i]&&a[u]>=a[f[x][i]]){
                x=f[x][i];
            }
        }
        f[u][0]=f[x][0];
    }
    for(int i=1; i<=20; i++){//更新倍增數組;
        f[u][i]=f[f[u][i-1]][i-1];
    }
    for(auto x: v[u]){
        if(x!=fa){
            dfs(x, u);
        }
    }
}
 
int main(){
    int n, q, x, y, z; scanf("%d%d", &n, &q);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
    }
    for(int i=1; i<n; i++){
        scanf("%d%d", &x, &y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    for(int i=1; i<=q; i++){
        scanf("%d%d%d", &x, &y, &z);
        v[n+i].push_back(x);//每個詢問新加一個結點連在u下面
        v[x].push_back(n+i);
        a[n+i]=z, R[i]=y;//這個查詢時的u節點對應的v節點;
    }
    dfs(1, 0);
    for(int i=1; i<=q; i++){
        int x=i+n, ans=0;//這次查詢時x節點在圖中代表的節點;
        for(int k=20; k>=0; k--){
            if(deep[f[x][k]]>=deep[R[i]]){//從u往上倍增找,位置不能超過v的位置;
                x=f[x][k]; ans+=(1<<k);//往上走了幾步,即寶石更新了幾次;
            }
        }
        printf("%d\n", ans);
    }
 
    return 0;
}
思路二:
#include<bits/stdc++.h>
#define LL long long
#define mid (l+r)/2
using namespace std;
 
struct Node{
    LL l, r;
    LL mx, pos;
}node[1000005];
 
void BT(LL i, LL l, LL r){
    node[i].l=l; node[i].r=r;
    if(l==r){
        node[i].mx=node[i].pos=0; return ;
    }
    BT(i<<1, l, mid); BT((i<<1)+1, mid+1, r);
}
 
void GX(LL i, LL x, LL y){
 
    if(node[i].l==node[i].r){
        node[i].pos=x;node[i].mx=y; return ;
    }
    if(x<=(node[i].l+node[i].r)/2) GX(i<<1, x, y);
    else GX((i<<1)+1, x, y);
 
    if(node[i<<1].mx>node[(i<<1)+1].mx){
        node[i].mx=node[i<<1].mx; node[i].pos=node[i<<1].pos;
    }
    else{
        node[i].mx=node[(i<<1)+1].mx; node[i].pos=node[(i<<1)+1].pos;
    }
}
 
int CXid(LL i, LL k){
 
    if(node[i].l==node[i].r) return node[i].l;
    if(node[(i<<1)+1].mx>k)  return CXid((i<<1)+1, k);
    else if(node[i<<1].mx>k) return CXid(i<<1, k);
    return 0;
}
 
LL mx=0, pos=0;
void CXmax(LL i, int l, int r){
 
    if(node[i].l==l&&node[i].r==r){
        if(node[i].mx>mx){
            mx=node[i].mx; pos=node[i].pos;
        }
        return ;
    }
    if(r<=(node[i].l+node[i].r)/2){
        CXmax(i<<1, l, r);     return ;
    }
    if(l>(node[i].l+node[i].r)/2){
        CXmax((i<<1)+1, l, r); return;
    }
    CXmax(i<<1, l, (node[i].l+node[i].r)/2);
    CXmax((i<<1)+1, (node[i].l+node[i].r)/2+1, r);
 
}
 
/*************************************************/
 
int a[200005], ans[200005], f[200005], b[200005], tot=0;
vector<vector<int> > v(200005);
vector<pair<int, int> > q[200005];
 
void DFS(int u, int fa){
    b[u]=++tot;
    int id=CXid(1, a[u]);//查詢鏈上距離u最進並且a[id]>a[u]的節點
    f[b[u]]=f[id]+1;
    GX(1, b[u], a[u]);//把u的影響加入線段樹
    for(auto x: q[u]){//處理查詢
        mx=0, pos=0;
        CXmax(1, b[x.first], b[u]);
        ans[x.second]=f[b[u]]-f[pos];
    }
    for(int x: v[u]){
        if(x!=fa){
            DFS(x, u);
        }
    }
    GX(1, b[u], 0);//刪除u的影響
    --tot;
}
 
int main(){
 
    int n, Q, x, y, c; scanf("%d%d", &n, &Q);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
    }
    for(int i=1; i<n; i++){
        scanf("%d%d", &x, &y);
        v[x].push_back(y); v[y].push_back(x);
    }
    BT(1, 1, n);
    for(int i=1; i<=Q; i++){
        scanf("%d%d%d", &x, &y, &c);
        a[n+i]=c;
        v[n+i].push_back(x); v[x].push_back(n+i);
        q[n+i].push_back({y, i});
    }
    DFS(1, 0);
    for(int i=1; i<=Q; i++){
        printf("%d\n", ans[i]);
    }
 
    return 0;
}
//思路三
#include <bits/stdc++.h>
using namespace std;
#define LL long long
 
int a[200005];
struct node{
    int v, id;
}st[200005];
vector<vector<int> > v(200005);
vector<vector<node> > q(200005);//查詢
int deep[200005], ans[200005], tot=0;
struct bc{
    int tot, x;//tot:棧的元素個數 改變的下標:x
    node t;//棧裏的內容
}b[200005];
 
int ER(int u, int v){
 
    int l=1, r=tot, pos1=1, pos2=0;
    while(l<=r){//最大第一個deep>=v的節點
        int mid=(l+r)/2;
        if(st[mid].id>=deep[v]){
            pos1=mid;
            r=mid-1;
        }
        else{
            l=mid+1;
        }
    }
    l=1; r=tot;
    while(l<=r){//最後一個a[pos2]>a[u]的節點
        int mid=(l+r)/2;
        if(st[mid].v>a[u]){
            pos2=mid;
            l=mid+1;
        }
        else{
            r=mid-1;
        }
    }
    return max(0, pos2-pos1+1);
}
 
void DFS(int u, int fa, int d){
    b[u].tot=tot;
    deep[u]=++d;//保存深度
    while(tot&&st[tot].v<=a[u]){//把節點u加入單調棧
        tot--;
    }
    b[u].x=++tot, b[u].t=st[tot];//保存單調棧的變化
    st[tot]=node{a[u], deep[u]};
 
//    printf("%d: ", u);
//    for(int i=1; i<=tot; i++){
//        printf("%d ", st[i].v);
//    }
//    printf("\n");
 
    for(auto x: q[u]){//處理查詢
        ans[x.id]=ER(u, x.v);
    }
 
    for(auto x: v[u]){
        if(x==fa) continue;
        DFS(x, u, d);
    }
    tot=b[u].tot;//恢復單調棧的變化
    st[b[u].x]=b[u].t;
}
 
int main(){
    int n, Q, x, y, c; scanf("%d%d", &n, &Q);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
    }
    for(int i=1; i<n; i++){
        scanf("%d%d", &x, &y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    for(int i=1; i<=Q; i++){//把查詢變成節點
        scanf("%d%d%d", &x, &y, &c);
        a[n+i]=c;
        v[n+i].push_back(x);
        v[x].push_back(n+i);
        q[n+i].push_back(node{y, i});
    }
    DFS(1, 0, 0);
    for(int i=1; i<=Q; i++){
        printf("%d\n", ans[i]);
    }
 
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章