\(\\\)

Description


给出一颗树,开始只有 \(1\) 号节点有标记。

  • \(\ C\ x\) 对 \(x\) 号节点打标记

  • \(\ Q\ x\) 查询 \(x\) 号节点深度最深的有标记的祖先

\(\\\)

Solution


  • 链剖做法:

    查询直到跳到第一个有权的重链上,线段树上二分即可。太板了不说了。

  • DFS序+线段树做法:

    一遍DFS求出DFS序,子树大小以及节点深度。

    用线段树维护DFS序,每个节点记录覆盖当前区间深度最深的节点编号。标记下放的时候只需选择深度更深的作为答案即可。注意设置根节点的深度,否则第一次的全局标记可能会无效。

    因为DFS序中一棵子树是连续的,所以标记可以看作整棵子树的区间覆盖操作。查询也很方便,在递归查找时下放标记即可。

    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 100010
    #define gc getchar
    #define Rg register
    #define mid ((l+r)>>1)
    using namespace std; inline int rd(){
    int x=0; bool f=0; char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return f?-x:x;
    } int n,m,tot,hd[N]; int cnt,s[N],d[N],dfn[N],sz[N]; struct edge{int to,nxt;}e[N]; inline void add(int u,int v){
    e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
    } void dfs(int u,int fa){
    dfn[u]=++cnt;
    s[cnt]=u; sz[u]=1;
    for(Rg int i=hd[u],v;i;i=e[i].nxt)
    if((v=e[i].to)!=fa){
    d[v]=d[u]+1;
    dfs(v,u);
    sz[u]+=sz[v];
    }
    } struct segment{ int root,ptr; inline int newnode(){return ++ptr;} struct node{int ls,rs,tag;}c[N<<1]; inline void build(int &rt,int l,int r){
    rt=newnode();
    if(l==r) return;
    build(c[rt].ls,l,mid);
    build(c[rt].rs,mid+1,r);
    } inline void pushdown(int rt){
    if(d[c[rt].tag]>d[c[c[rt].ls].tag]) c[c[rt].ls].tag=c[rt].tag;
    if(d[c[rt].tag]>d[c[c[rt].rs].tag]) c[c[rt].rs].tag=c[rt].tag;
    c[rt].tag=0;
    } inline void updata(int rt,int l,int r,int L,int R,int p){
    if(l>R||r<L) return;
    if(l>=L&&r<=R){
    if(d[p]>d[c[rt].tag]) c[rt].tag=p;
    return;
    }
    if(c[rt].tag) pushdown(rt);
    if(L<=mid) updata(c[rt].ls,l,mid,L,R,p);
    if(R>mid) updata(c[rt].rs,mid+1,r,L,R,p);
    } inline int query(int rt,int l,int r,int p){
    if(l==r) return c[rt].tag;
    if(c[rt].tag) pushdown(rt);
    if(p<=mid) return query(c[rt].ls,l,mid,p);
    else return query(c[rt].rs,mid+1,r,p);
    } }tree; int main(){
    n=rd(); m=rd();
    for(Rg int i=1,u,v;i<n;++i){
    u=rd(); v=rd();
    add(u,v); add(v,u);
    }
    d[1]=1; dfs(1,0);
    tree.build(tree.root,1,n);
    tree.updata(tree.root,1,n,1,n,1);
    char c; int x;
    while(m--){
    c=gc(); while(!isalpha(c)) c=gc();
    if(c=='Q') printf("%d\n",tree.query(tree.root,1,n,dfn[rd()]));
    else{x=rd();tree.updata(tree.root,1,n,dfn[x],dfn[x]+sz[x]-1,x);}
    }
    return 0;
    }
  • 并查集做法:

    我们用并查集指针代表当前最近的标记节点的方向,开始都指向父节点。标记一个点只需直接将指针指向自己,查询即找到集合代表元素。容易发现正着处理复杂度不对,因为要保证树的形态正确,所以我们不能使用并查集的优化方式。

    时光倒流。开始先把所有的标记打上,其余的点指向父节点。倒着模拟,遇到打标记就撤销标记,询问就是找到集合代表元素。可以使用路径压缩优化,因为只会撤销标记,任意时刻集合的代表元素必然是集合内的最优答案。

    注意一个节点可能被多次打标记,在最后一次撤销标记时我们再将其与父节点 merge 在一起。

    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 100010
    #define R register
    #define gc getchar
    #define mid ((l+r)>>1)
    using namespace std; inline int rd(){
    int x=0; bool f=0; char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return f?-x:x;
    } int n,m,tot,hd[N],fa[N],cnt[N]; struct edge{int to,nxt;}e[N<<1]; inline void add(int u,int v){
    e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
    } void dfs(int u){
    for(R int i=hd[u],v;i;i=e[i].nxt)
    if((v=e[i].to)!=fa[u]){fa[v]=u;dfs(v);}
    } struct Q{int op,x,ans;}q[N]; struct UFS{
    int f[N];
    UFS(){memset(f,0,sizeof(f));}
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    inline void merge(int x,int fa){f[find(x)]=find(fa);}
    }ufs; int main(){
    n=rd(); m=rd();
    for(R int i=1,u,v;i<n;++i){
    u=rd(); v=rd();
    add(u,v); add(v,u);
    }
    fa[1]=1;
    dfs(1); char c;
    for(R int i=1;i<=m;++i){
    c=gc(); while(!isalpha(c)) c=gc();
    q[i].op=(c=='C'); q[i].x=rd();
    if(q[i].op){ufs.f[q[i].x]=q[i].x;++cnt[q[i].x];}
    }
    for(R int i=1;i<=n;++i) if(!ufs.f[i]) ufs.f[i]=fa[i];
    for(R int i=m;i;--i)
    if(q[i].op){
    --cnt[q[i].x];
    if(!cnt[q[i].x]) ufs.merge(q[i].x,fa[q[i].x]);
    }
    else q[i].ans=ufs.find(q[i].x);
    for(R int i=1;i<=m;++i) if(!q[i].op) printf("%d\n",q[i].ans);
    return 0;
    }

[ HEOI 2016 ] 树的更多相关文章

  1. BZOJ 4551 HEOI 2016 树 (并查集)

    思路: 考虑时光倒流 这不就是并查集裸题了-----. //By SiriusRen #include <cstdio> #include <cstring> #include ...

  2. [HEOI 2016] sort

    [HEOI 2016] sort 解题报告 码线段树快调废我了= = 其实这题貌似暴力分很足,直接$STL$的$SORT$就能$80$ 正解: 我们可以二分答案来做这道题 假设我们二分的答案为$a$, ...

  3. [Luogu 4092] HEOI/TJOI2016 树

    [Luogu 4092] HEOI/TJOI2016 树 搜了树剖标签不知道怎么就跳出了个暴搜题啊! 管他既然做了就发上来吧- 有修改标签就向下搜并修改,遇到标签即停止. 这题是真的真的短. #inc ...

  4. 数据结构(并查集||树链剖分):HEOI 2016 tree

    [注意事项] 为了体现增强版,题目限制和数据范围有所增强: 时间限制:1.5s 内存限制:128MB 对于15% 的数据,1<=N,Q<=1000. 对于35% 的数据,1<=N,Q ...

  5. [HNOI 2016]树

    Description 题库链接 给你一棵 \(N\) 个节点根节点为 \(1\) 的有根树,结点的编号为 \(1\sim N\) :我们称这颗树为模板树.需要通过这棵模板树来构建一颗大树.构建过程如 ...

  6. [TJOI 2016&HEOI 2016]排序

    Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这 ...

  7. HEOI 2016 游记

    闲来无事,把这玩意儿补上. OI生涯中第一次正经的考试.挂的很惨. Day -1 不小心把机油(雾)sm惹毛了. 好像没啥别的事儿. Day 0 说好了上午直接去机房,然而临时说让我们上完前两节课再去 ...

  8. 字符串[未AC](后缀自动机):HEOI 2016 str

    超级恶心,先后用set维护right,再用主席树维护,全部超时,本地测是AC的.放心,BZOJ上还是1S限制,貌似只有常数优化到一定境界的人才能AC吧. 总之我是精神胜利了哦耶QAQ #include ...

  9. COGS 2274. [HEOI 2016] tree

    ★☆   输入文件:heoi2016_tree.in   输出文件:heoi2016_tree.out   简单对比时间限制:1 s   内存限制:128 MB 这道题数据弱到炸了 . 第一次做用树刨 ...

随机推荐

  1. redis03----集合 set 相关命令

    集合 set 相关命令 集合的性质: 唯一性,无序性,确定性 注: 在string和link的命令中,可以通过range 来访问string中的某几个字符或某几个元素 但,因为集合的无序性,无法通过下 ...

  2. Linux下配置rsync服务器

    一.简介 rsync是一个远程数据同步工具,可以快速同步多台主机间的文件.Rsync使用所谓的“Rsync算法”来使本地和远程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部分,而不是每次都 ...

  3. LoadRunner 技巧之 IP欺骗

    IP欺骗也是也loadrunner自带的一个非常有用的功能. 需要使用ip欺骗的原因:1.当某个IP的访问过于频繁,或者访问量过大是,服务器会拒绝访问请求,这时候通过IP欺骗可以增加访问频率和访问量, ...

  4. LA-4726 (斜率优化+单调队列)

    题意: 给定一个01序列,选一个长度至少为L 的连续子序列使其平均值最大;输出这个子序列的起点和终点;如果有多个答案,输出长度最小的,还有多个就输出第一个编号最小的; 思路: 用sum[i]表示[1, ...

  5. Power Crisis

    传送门 这道题被严重恶意评分了,实际应该是绿题. 因为其实我们只需要模拟即可.这里我们引入一种新的东西:约瑟夫环. 它能直接告诉你约瑟夫问题中最后一个存活下来的人是谁.(不过下标是从0开始的,实际使用 ...

  6. Myeclipse----Hibernate环境搭建

    使用myEclipse来生成hibernate 持久化类和映射文件 总体步骤:创建数据库----创建web工程----创建数据视图中的数据库-----导入hibernate框架需要的capabilit ...

  7. caffe从入门到放弃

    断断续续折腾ML近一年,写点博客记录这个坑.

  8. eclipse中更改配置使得switch语句不出错

    分别点击: windows---preference--->java---->compiler--->error/waring---->potential programmin ...

  9. 【197】PowerShell 通过 FTP 下载文件

    参考:使用 WGET 从FTP上下载文件 在 PowerShell 使用 wget2 工具,代码如下: wget2 ftp://user:password@192.168.14.31/1.jpg 其中 ...

  10. c语言sscanf总结

    1函数原型 int scanf(const char *format,[,argument]...) extern int sscanf(_const char*_restrict_s,const c ...