HDU 6394 Tree(dfs序+分块)

256 篇文章 0 订阅

Description

给出一棵 n n 个节点的有根树,根节点为1,到达第 i i 个点可以一次往上跳ai层,跳出树则停止,两种操作,第一种是查询 x x 点出发跳出该棵树需要的步数,第二种是修改ax

Input

第一行一整数 T T 表示用例组数,每组用例首先输入一整数n表示点数,然后输入 n n 个整数a1,...,an,之后输入一整数 m m 表示操作数,最后m行每行一个操作

(1n,m105,1ain) ( 1 ≤ n , m ≤ 10 5 , 1 ≤ a i ≤ n )

Output

对于每次 1 1 操作输出查询结果

Sample Input

1
4
1 2 3
1 1 1 1
3
1 4
2 3 2
1 4

Sample Output

4
3

Solution

由于每个点的dfs序必然大于往上跳的点的 dfs d f s 序,以 dfs d f s 序分块,维护三个值 In[u],Out[u],ans[u] I n [ u ] , O u t [ u ] , a n s [ u ] 分别表示 u u 在块内的要跳的下一个点、u跳出块后的点、 u u 跳出块需要的步数,在线倍增即可快速得到x点的第 y y 层祖先,对于查询操作,只需在块间跳累加对应的ans值即可,对于修改操作,首先修改 x x 点的三个值,之后对于x所处块内的所有点,按 dfs d f s 序从小到大维护这三个值,时间复杂度 O(mn+(m+n)logn) O ( m n + ( m + n ) l o g n )

Code

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
namespace fastIO 
{
    #define BUF_SIZE 100000
    //fread -> read
    bool IOerror=0;
    inline char nc() 
    {
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if(p1==pend) 
        {
            p1=buf;
            pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if(pend==p1) 
            {
                IOerror=1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) 
    {
        return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
    }
    inline void read(int &x) 
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        int sgn=1;
        if(ch=='-')sgn=-1;
        for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
        x=sgn*x;
    }
    #undef BUF_SIZE
};
using namespace fastIO;
#define maxn 100005
int T,n,q,a[maxn],dep[maxn],p[maxn][18];
int dfn[maxn],index,Id[maxn];
int In[maxn],Out[maxn],ans[maxn];
vector<int>g[maxn];
void dfs(int u)
{
    Id[index]=u;
    dfn[u]=index++;
    for(int i=0;i<g[u].size();i++)
    {
        dep[g[u][i]]=dep[u]+1;
        dfs(g[u][i]);
    }
}
int find(int u,int x)
{
    if(x>dep[u])return 0;
    for(int i=0;i<18;i++)
        if((x>>i)&1)u=p[u][i];
    return u;
}
int main()
{
    read(T);
    while(T--)
    {
        read(n);
        memset(p,0,sizeof(p));
        for(int i=1;i<=n;i++)g[i].clear();
        for(int i=2;i<=n;i++)
        {
            read(p[i][0]);
            g[p[i][0]].push_back(i);
        }
        for(int i=1;i<=n;i++)read(a[i]);
        for(int j=1;j<18;j++)
            for(int i=1;i<=n;i++)
                p[i][j]=p[p[i][j-1]][j-1];
        index=0;
        dep[1]=0;
        dfs(1);
        int m=(int)sqrt(n+0.5);
        for(int i=1;i<=n;i++)dfn[i]=dfn[i]/m+1;
        for(int i=0;i<n;i++)
        {
            int u=Id[i],v=find(u,a[u]);
            if(dfn[v]!=dfn[u])ans[u]=1,In[u]=0,Out[u]=v;
            else ans[u]=ans[v]+1,In[u]=v,Out[u]=Out[v];
        }
        read(q);
        while(q--)
        {
            int type,u,w;
            read(type);
            read(u);
            if(type==1)
            {
                int res=0;
                while(u)
                {
                    res+=ans[u];
                    u=Out[u];
                }
                printf("%d\n",res);
            }
            else
            {
                read(w);
                a[u]=w;
                int v=find(u,a[u]);
                if(dfn[u]!=dfn[v])ans[u]=1,In[u]=0,Out[u]=v;
                else ans[u]=ans[v]+1,In[u]=v,Out[u]=Out[v];
                for(int i=(dfn[u]-1)*m;i<dfn[u]*m;i++)
                {
                    v=Id[i];
                    if(In[v])ans[v]=ans[In[v]]+1,Out[v]=Out[In[v]];
                }
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值