BZOJ3306 树(线段树 + DFS序)

版权声明:转载请声明出处,谢谢支持 https://blog.csdn.net/Dreamstar_DS/article/details/82182509


时间限制:10s 空间限制:256MB

题目描述
给定一棵大小为 n 的有根点权树,支持以下操作:
  • 换根
  • 修改点权
 • 查询子树最小值

输入格式
  第一行两个整数 n, Q ,分别表示树的大小和操作数。
  接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
  接下来 m 行,为以下格式中的一种:
  • V x y表示把点x的权改为y
  • E x 表示把有根树的根改为点 x
  • Q x 表示查询点 x 的子树最小值

输出格式
  对于每个 Q ,输出子树最小值。

样例输入
3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1

样例输出

1
2
3
4

提示
  对于 100% 的数据:n, Q ≤ 10^5。
  


没想到做此题时,犯下了大量的低级错误。。。
首先这类题我们要利用DFS序使用线段树维护,这是本题的预备知识
对于本题而言,换根操作显然是关键点,如何在保留原树的情况下求出换根后对应子树的MIN呢,这里我们可以通过分情况讨论来解决
设rt为新根,x为查询节点
①如果rt == x,那么就相当于查整个树的MIN,我们输出t[1].min即可
②如果rt在x的子树内,相当于我们本来是从1号节点到x再到rt,现在我们要强制变为rt到x,显然变向的这一部分就不能用了,因此我们想要求到rt到x这条路径上离x最近的那个点,即为x的儿子之一·,然后砍掉这一区间即可,求剩下区间的MIN(因为已经“变向”)因此我们结合求lca里“向上跳”的方法来优化这一过程
③如果不满足上述情况,那么我们就不存在上文所说的变向的情况了,直接查x子树MIN

AC Code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define rg register
#define il inline
#define maxn 200005
#define lid id << 1
#define rid (id << 1) | 1
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
struct edge{
    int to , next;  
}e[maxn << 1];
struct tree{
    int l , r , min;    
}t[maxn << 2];
int head[maxn] , cnt , val[maxn] , f[maxn][22] , tot , dep[maxn] , q[maxn];
int l[maxn] , r[maxn];
void add(int u ,int v){
    e[++cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;  
}
void dfs(int u,int fa){
    l[u] = ++tot , q[tot] = u;
    dep[u] = dep[fa] + 1;
    f[u][0] = fa;
    for (rg int i = 1 ; i <= 20; ++i) 
        f[u][i] = f[f[u][i - 1]][i - 1];
    for (rg int i = head[u] ; i ; i = e[i].next){
        rg int to = e[i].to;
        if (to != fa){
            dfs(to , u);
        }
    }
    r[u] = tot;
}
void pushup(int id){
    t[id].min = min (t[lid].min , t[rid].min);  
}
void build(int id,int l,int r){
    t[id].l = l , t[id].r = r;
    if (l == r) {t[id].min = val[q[l]];return;}
    rg int mid = (l + r) >> 1;
    build(lid , l ,mid);
    build(rid , mid + 1 , r);
    pushup(id);
}
void update(int id,int pos,int x){
    if (pos == t[id].l && t[id].l == t[id].r) {t[id].min = x;return;}
    rg int mid = (t[id].l + t[id].r) >> 1;
    if (pos <= mid) update(lid , pos , x);
    else update(rid , pos , x);
    pushup(id);
}
int query(int id,int l,int r){
    if (l > r) return 1000000000;
    if (t[id].l == l && t[id].r == r) return t[id].min;
    rg int mid = (t[id].l + t[id].r) >> 1;
    if (r <= mid) return query(lid , l ,r);
    else if (l > mid) return query(rid , l ,r);
    else return min(query(lid , l , mid) , query(rid , mid + 1 , r));
}
char id[20];
int main(){
    int n = read(), m = read() , fa , x , y;
    for (rg int i = 1; i <= n;++i){
        fa = read() , val[i] = read();
        if (fa) add (fa , i);
    }
    int rt = 1;
    dfs(1 , 0);
    build(1 , 1 , n);
    for (rg int i = 1 ; i <= m ; ++i){
        scanf("%s",id);
        if (id[0] == 'E') rt = read();
        else if (id[0] == 'V') {
            x = read() , y = read();
            update(1 , l[x] , y);
        }
        else{
            x = read();
            if (x == rt) printf("%d\n",t[1].min);
            else if (l[x] <= l[rt] && r[rt] <= r[x]){
                rg int depth = dep[rt] - dep[x] - 1 , y = rt;
                for (rg int i = 0 ; i <= 20 ;++i) if (depth & (1 << i)) y = f[y][i];
                printf("%d\n",min ( query(1 , 1 , l[y] - 1) , query(1 , r[y] + 1 , n) ));   
            }
            else printf("%d\n",query(1 , l[x] , r[x]));
        }
    }
    return 0;
}   

猜你喜欢

转载自blog.csdn.net/Dreamstar_DS/article/details/82182509