[BZOJ4196][NOI2005]软件包管理器(链剖+dfs序)

本文详细介绍了如何利用树链剖分和线段树解决特定类型的查询与更新问题,通过dfs序的in和out值将子树转换为线段树上的连续区间,实现高效的区间操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
我是超链接
题解:
dfs序的in和out可以完美的把子树囊括在内,所以在维护的线段树上也是一段连续的区间,可以整体操作
首先关于安装问题,可以用状态0/1来表示
对于根到结点X的查询,非常容易
好了出于对树链剖分更深层次的理解,写一遍数组的意义:
in编号为i的点在线段树数组中的编号,sum编号为i的点在线段树中的存在的孩子个数【in是连接现实和虚拟的门。
添加软件包的时候,由于不是x的子树全部都要添加,必须呈链状修改
但删除就简单多了,呈团状子树全都没啦
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#define N 100000
#define MIN -19e+7
using namespace std;
int size[N*4],ww[N*4],son[N*4],fa[N*4],deep[N*2+5],delta[N*4+5];
int top[N*4],in[N*4],out[N*4],totw=0;
int sum[N*4],maxn[N*4],minn[N*4];
int next[N*2+5],point[N*2+5],v[N*2+5],tot=0,cnt=0,n;
void addline(int x,int y)
{
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void updata(int now){sum[now]=sum[now<<1]+sum[now<<1|1];}
void dfs_1(int now,int dep,int faa)
{
    deep[now]=dep;
    fa[now]=faa;
    size[now]=1;
    int maxx=MIN;
    for (int i=point[now];i;i=next[i])
      if (v[i]!=faa)
      {
        dfs_1(v[i],dep+1,now);
        size[now]+=size[v[i]];
        if (size[v[i]]>maxx)
        {
            maxx=size[v[i]];
            son[now]=v[i];
        }
      }
}
void dfs_2(int now,int faa)
{
    if (son[faa]!=now) top[now]=now;
    else top[now]=top[faa];
    in[now]=++cnt;
    if (son[now])
    {
        dfs_2(son[now],now);
        for (int i=point[now];i;i=next[i])
          if (v[i]!=son[now] && v[i]!=faa)
            dfs_2(v[i],now);
    }
    out[now]=cnt;
}
void pushdown(int now,int l,int r,int mid)
{
    if (delta[now]>=0)
    {
        delta[now<<1]=delta[now];
        delta[now<<1|1]=delta[now];
        sum[now<<1]=delta[now]*(mid-l+1);
        sum[now<<1|1]=delta[now]*(r-mid);
        delta[now]=-1;
    }
}
int qurry(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l && rrange>=r) return sum[now];    
    int mid=(l+r)>>1,ans=0;
    pushdown(now,l,r,mid);
    if (mid>=lrange) ans+=qurry(now<<1,l,mid,lrange,rrange);
    if (mid<rrange) ans+=qurry(now<<1|1,mid+1,r,lrange,rrange);
    return ans;
} 
void change(int now,int l,int r,int lrange,int rrange,int v)
{
    if (l>=lrange && rrange>=r)
    {
        delta[now]=v;
        sum[now]=v*(r-l+1);
        return;
    }
    int mid=(l+r)>>1;
    pushdown(now,l,r,mid);
    if (mid>=lrange)change(now<<1,l,mid,lrange,rrange,v);
    if (mid<rrange) change(now<<1|1,mid+1,r,lrange,rrange,v);
    updata(now);
}
int work(int u,int v,int id)
{
    int f1=top[u],f2=top[v],summ=0;
    while (f1!=f2)
    {
        if (deep[f1]<deep[f2]) 
        {
            swap(f1,f2); swap(u,v);
        }
        if (id==1) summ+=qurry(1,1,n,in[f1],in[u]);
        else change(1,1,n,in[f1],in[u],1);
        u=fa[f1];
        f1=top[u];
    }
    if (in[u]<in[v]) swap(u,v);
    if (id==1) summ+=qurry(1,1,n,in[v],in[u]);
    else change(1,1,n,in[v],in[u],1);
    return summ;
}
int main()
{
    int i,x,q;
    scanf("%d",&n);
    for (i=1;i<=n-1;i++)
    {
        scanf("%d",&x);
        addline(i+1,x+1);
    }
    dfs_1(1,1,0);
    dfs_2(1,0);
    memset(delta,128,sizeof(delta));
    scanf("%d",&q);
    for (i=1;i<=q;i++)
    {
        char st[20];int ans=0;
        scanf("%s%d",st,&x);
        x++;
        if (st[0]=='i')
        {
            ans=work(1,x,1);
            printf("%d\n",deep[x]-ans);
            ans=work(1,x,2);
        }
        else
        {
            ans=qurry(1,1,n,in[x],out[x]);
            printf("%d\n",ans);
            change(1,1,n,in[x],out[x],0);
        }
    }  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值