bzoj4551: [Tjoi2016&Heoi2016]树

本文介绍了一种解决特定树形结构问题的方法,通过离线并查集、LCT和线段树等数据结构来优化查询效率。重点讨论了一个具体的编程挑战案例,详细解析了如何利用线段树进行区间最大值更新和单点查询的操作。

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=4551

题解

  还行吧这道题,不是特别傻逼。
  一个点被标记,显然会影响到子树中的所有点。
  显然每次查询会选择这个点到根的路径上深度最深的那个祖先点作为答案。

做法一

  离线并查集,ftm用了这种方法,倒序变成删除标记。剩下的细节请读者思考。

做法二

  LCT直接暴力,一开始想用这个,但是看到题目下方:新加了九组测试数据,我就知道它想卡这个。弃疗。

做法三

  这个算法我感觉长得比较像正解。
  算出dfs序,每次修改,就把这个点的子树对这个点的深度取个max。
  线段树区间取max,单点查询。

代码

//线段树 
#include <cstdio>
#include <algorithm>
#define maxn 200010
using namespace std;
int tid[maxn], ltid[maxn], rtid[maxn], untid[maxn], N, Q, nex[maxn], to[maxn],
    head[maxn], tot, ndtot, deep[maxn];
inline void adde(int a, int b){to[++tot]=b;nex[tot]=head[a];head[a]=tot;}
struct segtree{int l, r, tag, max; segtree *ch[2];}pool[maxn], *root;
inline int read(int x=0)
{
    char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();
    return x;
}
inline char readc()
{
    char c=getchar();
    while(c!='Q' and c!='C')c=getchar();
    return c;
}
void dfs(int pos)
{
    int p;
    ltid[pos]=tid[pos]=++tid[0];untid[tid[pos]]=pos;
    for(p=head[pos];p;p=nex[p])deep[to[p]]=deep[pos]+1, dfs(to[p]);
    rtid[pos]=tid[0];
}
void build(segtree *p, int l, int r)
{
    int mid=(l+r)>>1;
    p->l=l, p->r=r;
    if(l==r)return;
    build(p->ch[0]=pool+ ++ndtot,l,mid);
    build(p->ch[1]=pool+ ++ndtot,mid+1,r);
}
void init()
{
    int a, b, i;
    N=read(), Q=read();
    for(i=1;i<N;i++)a=read(), b=read(), adde(a,b);
    deep[1]=1, dfs(1);
    build(root=pool+ ++ndtot,1,N);
}
inline void u(int a, int &b){if(deep[a]>deep[b])b=a;}
inline void tag(segtree *p, int tag){u(tag,p->tag);}
inline void pushdown(segtree *p)
{
    u(p->tag,p->max);
    if(p->ch[0])tag(p->ch[0],p->tag),tag(p->ch[1],p->tag);
}
inline void update(segtree *p)
{
    if(!p->ch[0])return;
    pushdown(p->ch[0]), pushdown(p->ch[1]);
    if(deep[p->ch[0]->max]>deep[p->ch[1]->max])p->max=p->ch[0]->max;
    else p->max=p->ch[1]->max;
}
void segchg(segtree *p, int l, int r, int a)
{
    pushdown(p);
    int mid=(p->l+p->r)>>1;
    if(l<=p->l and r>=p->r){tag(p,a);return;}
    if(l<=mid)segchg(p->ch[0],l,r,a);
    if(r>mid)segchg(p->ch[1],l,r,a);
    update(p);
}
int segmax(segtree *p, int l, int r)
{
    pushdown(p);
    int mid=(p->l+p->r)>>1, ans=0;
    if(l<=p->l and r>=p->r)return p->max;
    if(l<=mid)u(segmax(p->ch[0],l,r),ans);
    if(r>mid)u(segmax(p->ch[1],l,r),ans);
    return ans;
}
void work()
{
    int pos;
    char type;
    tag(root,1);
    while(Q--)
    {
        type=readc();
        pos=read();
        if(type=='C')segchg(root,ltid[pos],rtid[pos],pos);
        else printf("%d\n",segmax(root,tid[pos],tid[pos]));
    }
}
int main()
{
    init();
    work();
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值