【BZOJ-3306】树 线段树 + DFS序

3306: 树

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit:
792  Solved: 262
[Submit][Status][Discuss]

Description

给定一棵大小为 n 的有根点权树,支持以下操作:

  • 换根
  • 修改点权 
     • 查询子树最小值

Input

  第一行两个整数 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 的子树最小值

Output

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

Sample Input

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

Sample Output

1
2
3
4

HINT

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

Source

Solution

有道很类似的题目,不过是树链修改

那道题去要树链剖分,而这里只需要线段树维护一下DFS序即可

先以1为根做DFS和建线段树维护dfs序

换根操作只需要讨论一下:

若root=x,那么显然查询全树min

若LCA(root,x)!=x,那么显然毫无影响

若LCA(root,x)==x,那么发现对答案产生了影响,除了x-->root的那个子树,其余都变成了x的子树,那么我们倍增出那个不属于的子树中最接近x的节点,然后统计不包含这棵子树的答案即可

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define MAXN 100010
int N,Q,val[MAXN];
struct EdgeNode{int next,to;}edge[MAXN<<1];
int head[MAXN],cnt;
void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void InsertEdge(int u,int v) {if (u==0) return; AddEdge(u,v); AddEdge(v,u);}
int pl[MAXN],dfn,pr[MAXN],dfsn[MAXN],deep[MAXN],father[MAXN][21],root;
void DFS(int now,int last)
{
    pl[now]=++dfn; dfsn[dfn]=now;
    for (int i=1; i<=20; i++)
        if (deep[now]>=(1<<i))
            father[now][i]=father[father[now][i-1]][i-1];
        else
            break;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=last)
            {
                deep[edge[i].to]=deep[now]+1;
                father[edge[i].to][0]=now;
                DFS(edge[i].to,now);
            }
    pr[now]=dfn;
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int dd=deep[x]-deep[y];
    for (int i=0; i<=20; i++)
        if (dd&(1<<i)) x=father[x][i];
    for (int i=20; i>=0; i--)
        if (father[x][i]!=father[y][i])
            x=father[x][i],y=father[y][i];
    if (x==y) return x; else return father[x][0];
}
struct SegmentTreeNode{int l,r,minn;}tree[MAXN<<2];
inline void Update(int now) {tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);}
void BuildTree(int now,int l,int r)
{
    tree[now].l=l; tree[now].r=r;
    if (l==r) {tree[now].minn=val[dfsn[l]]; return;}
    int mid=(l+r)>>1;
    BuildTree(now<<1,l,mid);
    BuildTree(now<<1|1,mid+1,r);
    Update(now);
}
void Change(int now,int pos,int D)
{
    int l=tree[now].l,r=tree[now].r;
    if (l==r) {tree[now].minn=D; return;}
    int mid=(l+r)>>1;
    if (pos<=mid) Change(now<<1,pos,D);
        else Change(now<<1|1,pos,D);
    Update(now);
}
int Query(int now,int L,int R)
{
    if (R<L) return 0x7fffffff;
    int l=tree[now].l,r=tree[now].r;
    if (L==l && R==r) return tree[now].minn;
    int mid=(l+r)>>1,re=0x7fffffff;
    if (R<=mid) return Query(now<<1,L,R);
        else if (L>mid) return Query(now<<1|1,L,R);
            else return min(Query(now<<1,L,mid),Query(now<<1|1,mid+1,R));
    return re;
}
void ChangeRoot(int x)  {root=x;}
int GetAns(int x)
{
    int lca=LCA(root,x);
    if (x==root) return Query(1,1,N);
    if (pl[x]<=pl[root] && pr[x]>=pr[root])
        {
            int dd=deep[root]-deep[x]-1,y=root;
            for (int i=0; i<=20; i++)
                if (dd&(1<<i)) y=father[y][i];
            return min(Query(1,1,pl[y]-1),Query(1,pr[y]+1,dfn));
        }
    return Query(1,pl[x],pr[x]);
}
int main()
{
    N=read(); Q=read();
    for (int fa,i=1; i<=N; i++) fa=read(),InsertEdge(fa,i),val[i]=read();
    DFS(1,0);  root=1;
    BuildTree(1,1,dfn);
    while (Q--)
        {
            char opt[10]; scanf("%s",opt+1);
            int x,y;
            switch (opt[1])
                {
                    case ‘V‘ : x=read(),y=read(); Change(1,pl[x],y); break;
                    case ‘E‘ : x=read(); ChangeRoot(x); break;
                    case ‘Q‘ : x=read(); printf("%d\n",GetAns(x)); break;
                }
        }
    return 0;
} 
时间: 2024-10-24 08:39:41

【BZOJ-3306】树 线段树 + DFS序的相关文章

【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序

[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 我们把纸上打印出来的字符串从1开始顺序编号,一直到n.打字机有一个非

luogu SP8093 后缀自动机+树状数组+dfs序

这题解法很多,简单说几个: 1. 线段树合并,时间复杂度是 $O(nlog^2n)$ 的. 2. 暴力跳 $fail,$ 时间复杂度 $O(n\sqrt n),$ 比较暴力. 3. 建立后缀树后在 $dfs$ 序上数点,时间复杂度为 $O(nlogn),$ 十分优秀. Code: #include <bits/stdc++.h> #define N 200007 #define setIO(s) freopen(s".in","r",stdin) , f

Codeforces 29D Ant on the Tree 树的遍历 dfs序

题目链接:点击打开链接 题意: 给定n个节点的树 1为根 则此时叶子节点已经确定 最后一行给出叶子节点的顺序 目标: 遍历树并输出路径,要求遍历叶子节点时按照给定叶子节点的先后顺序访问. 思路: 给每个节点加一个优先级. 把最后一个叶子节点到父节点的路径上的点优先级改为1 把倒数第二个叶子节点到父节点的路径上的点优先级改为2 如此每个点就有一个优先级,每个访问儿子节点时先访问优先级大的即可 对于无解的判断:得到的欧拉序列不满足输入的叶子节点顺序即是无解. #include <cstdio> #

UCF Local Programming Contest 2018 E题(树状数组+dfs序)

如果这道题没有一个限制,那么就是一道树状数组+dfs序的裸题 第一个请求或许会带来困惑,导致想要动态建树,如果真的动态修改树,那么dfs序必定会改变,很难维护,并且数据很大,暴力应该会T 所以不妨先把全部的节点建好,这样只需要求一次dfs序,而对于第一种操作 我们只需要再那个位置减去在他之前的dfs序的bouns求和,并在这个的后一个位置+回来,这样就有这个点被修改,并且成为了一个新点,等同于要求的操作 #include<iostream> #include<cstdio> #in

#树# #线段树#

线段树 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 模板: 建树 1 void Pushup(int rt){//根节点

HDU 4417 Super Mario ( 超级马里奥 + 主席树 + 线段树/树状数组离线处理 + 划分树 )

HDU 4417 - Super Mario ( 主席树 + 线段树/树状数组离线处理 + 划分树 ) 这道题有很多种做法,我先学习的是主席树.后面陆续补上线段树离线和划分树 题目大意就是给定一个区间给定一个数列,每次要求你查询区间[L,R]内不超过K的数的数量 主席树做法: 最基本的是静态第k大,这里是求静态的 <= K,差不多,在Query操作里面需要修改修改 先建立size棵主席树,然后询问的时候统计的是 第R棵主席树中[1,K]的数量 - 第L-1棵主席树中[1,K]的数量 注意这里下标

BZOJ 3779 重组病毒 LCT+线段树(维护DFS序)

原题干(由于是权限题我就直接砸出原题干了,要看题意概述的话在下面): Description 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒.实验在一个封闭的局域网内进行.局域网内有n台计算机,编号为1~n.一些计算机之间通过网线直接相连,形成树形的结构.局域网中有一台特殊的计算机,称之为核心计算机.根据一些初步的研究,研究员们拟定了一个一共m步的实验.实验开始之前,核

【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\),问你答案是多少. \(n\leq {10}^5,k\leq {10}^9\) 题解 设\(l\)为这棵树的叶子个数,显然当\(k>\)树的深度时答案都是\(l\). 下面要证明:答案是\(O(l+\frac{n-l}{k})\)的. 我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网

codeforces 343D Water Tree 树链剖分 dfs序 线段树 set

题目链接 这道题主要是要考虑到同一棵子树中dfs序是连续的 然后我就直接上树剖了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=600005; 4 5 struct Node 6 { 7 int l,r; 8 int value; 9 void init() 10 { 11 l=r=value=0; 12 } 13 }tree[4*MAXN]; 14 vector<int>nei[MAXN]