HDOJ5692解题报告【dfs序+线段树】

题目地址:

  http://acm.hdu.edu.cn/showproblem.php?pid=5692

题目概述:

  中文题面就不赘述了。

大致思路:

  这个题给出的是一棵树,我们可以使用dfs序将这棵树处理成一条链,然后对这条链来进行信息维护和查询。

  有两种操作,0 x是询问从0出发(题目保证0为树根)经过x的路径中的最大权值,1 x y是将点x的权值修改成y,这时我们用线段树来维护一个d[i]表示点i到0点的权值和。

  对于第一种操作就是查询x及其子树上的最大值,经过dfs序的处理之后这是一段连续的区间,求一个最大值即可。

  对于第二种操作我们只需要在x及其子树上add一个y-a[x]即可。

  注意可能爆栈,记得按题目里的提示处理。

复杂度分析:

  dfs序是O(n),线段树查询跟区间修改是O(logn),建树O(nlogn),综上O(nlogn*T)

代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <ctime>
#include <map>
#include <assert.h>
#include <stack>
#include <set>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

#define sacnf scanf
#define scnaf scanf
#define maxn 100010
#define maxm 18
//#define inf 1061109567
const long long inf=1e15;
#define INF 0x3f3f3f3f
#define Eps 0.000001
const double PI=acos(-1.0);
#define mod 1000000007
#define MAXNUM 10000
#define For(i,j,k) for(int (i)=(j);(i)<=(k);(i)++)
#define mes(a,b) memset((a),(b),sizeof(a))
#define scanfi(a) scanf("%d",&(a))
typedef long long ll;
typedef unsigned long long ulld;
void Swap(int &a,int &b) {int t=a;a=b;b=t;}
ll Abs(ll x) {return (x<0)?-x:x;}
struct node
{
    ll Add,Max;
} tree[maxn*3];

vector<int> G[maxn];
int dfs_clock,pre[maxn],low[maxn],turn[maxn];
ll d[maxn],a[maxn];

void dfs(int u,ll sum)
{
    if(pre[u]) return;
    pre[u]=++dfs_clock;
    int len=G[u].size();
    For(i,0,len-1)
    {
        int v=G[u][i];
        dfs(v,sum+a[u]);
    }
    low[u]=dfs_clock;d[pre[u]]=sum+a[u];
    turn[u]=pre[u];
}

void build_tree(int l,int r,int dir)
{
    if(l==r)
    {
        tree[dir].Add=0;
        tree[dir].Max=d[l];
        return;
    }
    int m=(l+r)>>1;
    build_tree(l,m,dir*2);
    build_tree(m+1,r,dir*2+1);
    tree[dir].Add=0;
    tree[dir].Max=max(tree[dir*2].Max,tree[dir*2+1].Max);
}

void add_in(int dir,ll val)
{
    tree[dir].Add+=val;
    tree[dir].Max+=val;
}

void push_down(int dir)
{
    if(tree[dir].Add!=0)
    {
        add_in(dir*2,tree[dir].Add);
        add_in(dir*2+1,tree[dir].Add);
        tree[dir].Add=0;
    }
}

void maintain(int dir)
{
    tree[dir].Max=max(tree[dir*2].Max,tree[dir*2+1].Max);
}

void add(int l,int r,int dir,int al,int ar,ll val)
{
    if(al<=l&&r<=ar) {add_in(dir,val);return;}
    push_down(dir);
    int m=(l+r)>>1;
    if(al<=m) add(l,m,dir*2,al,ar,val);
    if(ar>m) add(m+1,r,dir*2+1,al,ar,val);
    maintain(dir);
}

ll ans;

void query(int l,int r,int dir,int ql,int qr)
{
    if(ql<=l&&r<=qr) {ans=max(ans,tree[dir].Max);return;}
    push_down(dir);
    int m=(l+r)>>1;
    if(ql<=m) query(l,m,dir*2,ql,qr);
    if(qr>m) query(m+1,r,dir*2+1,ql,qr);
    maintain(dir);
}

int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    //clock_t st=clock();
    int T;scanfi(T);
    For(kase,1,T)
    {
        printf("Case #%d:\n",kase);
        int n,m;scanf("%d%d",&n,&m);
        For(i,1,n)
        {
            G[i].clear();d[i]=turn[i]=0;
            pre[i]=low[i]=dfs_clock=0;
        }
        For(i,1,n-1)
        {
            int x,y;scanf("%d%d",&x,&y);
            x++;y++;G[x].push_back(y);
            G[y].push_back(x);
        }
        For(i,1,n) scanf("%lld",&a[i]);
        dfs(1,0);build_tree(1,n,1);
        //cout<<endl;For(i,1,n) printf("%d\n",d[i]);cout<<endl;
        int opt,x;ll y;
        while(m--)
        {
            scanfi(opt);
            if(opt==0)
            {
                scanf("%d%lld",&x,&y);x++;
                add(1,n,1,pre[x],low[x],y-a[x]);a[x]=y;
            }
            else if(opt==1)
            {
                scanfi(x);x++;ans=-inf;
                query(1,n,1,pre[x],low[x]);
                printf("%lld\n",ans);
            }
        }
    }
    //clock_t ed=clock();
    //printf("\n\nTime Used : %.5lf Ms.\n",(double)(ed-st)/CLOCKS_PER_SEC);
    return 0;
}
时间: 2024-10-15 16:06:02

HDOJ5692解题报告【dfs序+线段树】的相关文章

POJ 3321 DFS序+线段树

单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4:   5: #include <cstdio> 6: #include <cstring> 7: #include <cctype> 8: #include <algorithm> 9: #include <vector> 10: #include <iostream> 1

codevs1228 (dfs序+线段树)

总结: 第一次遇到dfs序的问题,对于一颗树,记录节点 i 开始搜索的序号 Left[i] 和结束搜索的序号 Righti[i],那么序号在 Left[i] ~ Right[i] 之间的都是节点 i 子树上的节点. 并且此序号与线段树中 L~R 区间对应,在纸上模拟了几遍确实如此,但暂时还未理解为何对应. 此题就是dfs序+线段树的裸题 代码: #include<iostream> #include<vector> #include<cstring> #include&

[BZOJ 3306]树(dfs序+线段树+倍增)

Description 给定一棵大小为 n 的有根点权树,支持以下操作: • 换根 • 修改点权 • 查询子树最小值 Solution 单点修改子树查询的话可以想到用dfs序+线段树来处理,换根的处理画一画图应该可以明白: 如果查询的x是当前的根rt,直接返回整棵树的min 如果rt在x的子树中,用倍增的方法找到离x最近的rt的祖先t,整棵树除t的子树以外的部分就是x当前根下的子树 如果rt不在x的子树中,查询x原来的子树的min值 #include<iostream> #include<

Educational Codeforces Round 6 E dfs序+线段树

题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili上电子科技大学发的视频学习的 将一颗树通过dfs编号的方式 使每个点的子树的编号连在一起作为相连的区间 就可以配合线段树搞子树 因为以前好像听说过 线段树可以解决一种区间修改和查询区间中不同的xx个数...所以一下子就想到了... 但是我不会写线段树..只会最简单的单点修改区间查询...不会用延迟标

【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心

3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 130[Submit][Status][Discuss] Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状结构:开始游戏时在根节点(共通线)

【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]

Manthan, Codefest 16(G. Yash And Trees(dfs序+线段树))

题目链接:点击打开链接 题意:给你一棵树, 根结点为1, q组操作, 每组操作有两种, 一种是对一个结点的所有子树结点的值全部+1, 另一种是查询一个结点的子树结点上值%m的余数为素数的个数. 思路:对于第一个操作, 我们可以想到用dfs序给树重新标号, 使得一个结点的子树结点为相邻的一条线段, 这样,就可以很容易的用线段树进行处理了.  对于第二个操作, 为了维护一个区间内的值, 我们可以用bitset作为结点信息.  我们可以开一个m位的bitset, 对于每个位, 1表示这个数在此区间中,

cdoj 574 High-level ancients dfs序+线段树

High-level ancients Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/574 Description Love8909 is keen on the history of Kingdom ACM. He admires the heroic undertakings of Lxhgww and Haibo. Inspired by those sagas, L