zoukankan      html  css  js  c++  java
  • [HEOI2016/TJOI2016]树

    题目概述

    题目描述

    在 2016 年,佳媛姐姐刚刚学习了树,非常开心。

    现在他想解决这样一个问题:给定一颗有根树,根为 (1) ,有以下两种操作:

    1. 标记操作:对某个结点打上标记。(在最开始,只有结点 (1) 有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)

    2. 询问操作:询问某个结点最近的一个打了标记的祖先。(这个结点本身也算自己的祖先)

      你能帮帮她吗?

    输入输出格式

    输入格式

    第一行两个正整数 (N)(Q) 分别表示节点个数和操作次数。

    接下来 (N-1) 行,每行两个正整数 (u,v \,\, (1 leqslant u,v leqslant n)) 表示 (u)(v) 有一条有向边。

    接下来 (Q) 行,形如 oper numoperC 时表示这是一个标记操作, operQ 时表示这是一个询问操作。

    输出格式

    输出一个正整数,表示结果

    输入输出样例

    输入样例 #1
    5 5 
    1 2 
    1 3 
    2 4 
    2 5 
    Q 2 
    C 2 
    Q 2 
    Q 5 
    Q 3
    
    输出样例 #1
    1
    2
    2
    1
    

    数据范围

    (30\%) 的数据,(1 leqslant N, Q leqslant 1000)(70\%) 的数据,(1 leqslant N, Q leqslant 10000)(100\%) 的数据,(1 leqslant N, Q leqslant 100000)

    解题报告

    题意理解

    1. 刚开始根节点有标记
    2. 找到一个点祖辈上面离他最近的标记
    3. 有修改操作,每次给一个点打标记

    算法解析

    只要有修改操作,而且是树上快速查询,我们总能够想到代码量著名的树链剖分算法。

    这道题目,线段树需要支持,区间最值操作

    为什么是区间最值啊?

    因为我们知道,对于一个点上面的点,肯定是深度越深,离他越近。

    因此我们不妨令线段树,存储一个节点的深度。或者说是,DFS序

    因为对于一条链而言,越深的点,他们的DFS序列将会是越大的,

    那么我们就可以查询区间最大值,找到离这个点最近的祖先标记,于是一切都迎刃而解了。

    这就是一个树链剖分的模板题目了,不过我们这里使用的代码是zkw线段树,也算是一种新型尝试了。

    而且我们将两个搜索,融合在一起,成为一个搜索呢。


    #include <bits/stdc++.h>
    using namespace std;
    const int N=100200,P=1<<17;
    vector<int> g[N];
    int dfn[N],pre[N],size[N],cnt,x,y,p,n,q,sum[P<<1];
    void dfs(int x)
    {
    	size[x]=1;
    	dfn[x]=++cnt;
    	pre[cnt]=x;
    	for(int y:g[x])
    	{
    		if(!size[y])
    		{
    			dfs(y);
    			size[x]+=size[y];
    		}
    	}
    }
    inline void Update(int l, int r, int x)//zkw线段树可还行
    {
    	for(l+=P-1, r+=P+1; l^r^1; l>>=1, r>>=1)
    	{
    		if(~l&1)
    			sum[l^1]=max(sum[l^1],x);
    		if(r&1)
    			sum[r^1]=max(sum[r^1],x);
    	}
    }
    inline int Query(int p)
    {
    	int ans=0;
    	for(p+=P; p; p>>=1)
    		ans=max(ans,sum[p]);
    	return ans;
    }
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for(int i=1; i<n; i++)
    	{
    		scanf("%d%d",&x,&y);
    		g[x].push_back(y);
    	}
    	dfs(1);
    	sum[1]=1;
    	for(int i=1; i<=q; i++)
    	{
    		char opt[10];
    		scanf("%s%d",&opt,&x);
    		if(opt[0]=='C')
    			Update(dfn[x],dfn[x]+size[x]-1,dfn[x]);
    		else
    			printf("%d
    ",pre[Query(dfn[x])]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    cha[] strrev(char[])
    线段树入门了解
    求最大严格递增序列
    素数环
    20181016-10 每周例行报告
    第五周例行报告
    作业要求 20180925-1 每周例行报告
    作业要求20180925-2 功能测试
    作业要求 20180925-3 效能分析
    作业要求20180925-4 单元测试,结对
  • 原文地址:https://www.cnblogs.com/gzh-red/p/11831646.html
Copyright © 2011-2022 走看看