每日一题 3月31日 城市网络 树上倍增 or DFS+线段树 or DFS+单调栈

3 篇文章 0 订阅
2 篇文章 0 订阅

题目链接: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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值