BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树

题目描述

在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下
两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个
结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
先)你能帮帮他吗?

输入

输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v
有一条有向边接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询
问操作对于每次询问操作,1 ≤ N, Q ≤ 100000。

输出

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

样例输入

5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3

样例输出

1
2
2
1
 
这道题有两种做法(据说不止两种,本蒟蒻只会这两种qwq)。
先来讲第一种:dfs序+线段树。
假设我们对一个点进行了标记,那么可能会影响哪些点的答案?
没错就是它的子树中所有点,因为一个点的子树在dfs序上是一段区间,我们以dfs序建线段树,每个点维护影响这个区间的深度最大的标记点是谁,那么每次修改时区间修改子树区间,将标记永久化,查询时单点查询,在线段树上沿途的标记中取深度最大的就是答案。
再来说说比较麻烦的一种做法:树链剖分+线段树。
对于线段树的每个点维护区间中被打标记的最深的点,每次修改时单点修改,查询时利用树链剖分往上爬重链,查询重链在线段树上的区间,只要有答案就输出。
dfs序+线段树
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int x,y;
int tot;
int num;
char ch[2];
int f[100010];
int s[100010];
int d[100010];
int t[100010];
int to[200010];
int mx[800010];
int next[200010];
int head[100010];
void add(int x,int y)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
void dfs(int x)
{
    s[x]=++num;
    d[x]=d[f[x]]+1;
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x])
        {
            f[to[i]]=x;
            dfs(to[i]);
        }
    }
    t[x]=num;
}
int cmp(int x,int y)
{
    if(d[x]>d[y])
    {
        return x;
    }
    else
    {
        return y;
    }
}
void change(int rt,int l,int r,int L,int R,int k)
{
    if(L<=l&&r<=R)
    {
        mx[rt]=cmp(mx[rt],k);
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid)
    {
        change(rt<<1,l,mid,L,R,k);
    }
    if(R>mid)
    {
        change(rt<<1|1,mid+1,r,L,R,k);
    }
}
int query(int rt,int l,int r,int k)
{
    if(l==r)
    {
        return mx[rt];
    }
    int mid=(l+r)>>1;
    int res=mx[rt];
    if(k<=mid)
    {
        return cmp(res,query(rt<<1,l,mid,k));
    }
    else
    {
        return cmp(res,query(rt<<1|1,mid+1,r,k));
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1);
    change(1,1,n,1,n,1);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ch);
        scanf("%d",&x);
        if(ch[0]=='C')
        {
            change(1,1,n,s[x],t[x],x);
        }
        else
        {
            printf("%d\n",query(1,1,n,s[x]));
        }
    }
}

树链剖分+线段树

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int x,y;
int tot;
int num;
char ch[2];
int f[100010];
int s[100010];
int d[100010];
int q[100010];
int to[200010];
int mx[800010];
int son[100010];
int top[100010];
int size[100010];
int next[200010];
int head[100010];
void add(int x,int y)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
void dfs(int x)
{
    d[x]=d[f[x]]+1;
    size[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x])
        {
            f[to[i]]=x;
            dfs(to[i]);
            size[x]+=size[to[i]];
            if(size[to[i]]>size[son[x]])
            {
                son[x]=to[i];
            }
        }
    }
}
void dfs2(int x,int tp)
{
    s[x]=++num;
    q[num]=x;
    top[x]=tp;
    if(son[x])
    {
        dfs2(son[x],tp);
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x]&&to[i]!=son[x])
        {
            dfs2(to[i],to[i]);
        }
    }
}
int cmp(int x,int y)
{
    if(d[x]>d[y])
    {
        return x;
    }
    else
    {
        return y;
    }
}
void pushup(int rt)
{
    mx[rt]=cmp(mx[rt<<1],mx[rt<<1|1]);
}
void change(int rt,int l,int r,int k,int v)
{
    if(l==r)
    {
        mx[rt]=v;
        return ;
    }
    int mid=(l+r)>>1;
    if(k<=mid)
    {
        change(rt<<1,l,mid,k,v);
    }
    else
    {
        change(rt<<1|1,mid+1,r,k,v);
    }
    pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
    {
        return mx[rt];
    }
    int mid=(l+r)>>1;
    if(L>mid)
    {
        return query(rt<<1|1,mid+1,r,L,R);
    }
    if(R<=mid)
    {
        return query(rt<<1,l,mid,L,R);
    }
    else
    {
        return cmp(query(rt<<1,l,mid,L,R),query(rt<<1|1,mid+1,r,L,R));
    }
}
int ask(int x)
{
    int res;
    while(top[x]!=1)
    {
        res=query(1,1,n,s[top[x]],s[x]);
        if(res!=0)
        {
            return res;
        }
        x=f[top[x]];
    }
    res=query(1,1,n,1,s[x]);
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1);
    dfs2(1,1);
    change(1,1,n,1,1);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ch);
        scanf("%d",&x);
        if(ch[0]=='C')
        {
            change(1,1,n,s[x],x);
        }
        else
        {
            printf("%d\n",ask(x));
        }
    }
}
posted @ 2018-10-10 10:24  The_Virtuoso  阅读(162)  评论(0编辑  收藏  举报