4196: [Noi2015]软件包管理器

本文介绍了一种处理有根树上特定操作的方法,利用线段树进行高效更新与查询。针对两种主要操作——路径更新和子树清零,文章详细展示了如何通过重链剖分和线段树实现快速响应,同时分享了代码实现细节。

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

题目链接

题目大意:给出一个n个点的有根树,和m次操作;
初始时树上所有结点权值均为0;
1.将根到x结点的所有结点权值置为1,并输出这次修改了多少个元素;
2.将x结点的子树中所有结点权值置为0,并输出这次修改了多少个元素

题解:剖剖剖

我的收获:注意编号从0/1开始

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int M=100005;

int q,n,t,tim,head[M];
int dfn[M],end[M];

struct edge{int to,nex;}e[M<<1];

void add(int u,int v){e[t].to=v,e[t].nex=head[u],head[u]=t++;}

namespace stree{

#define ls x<<1
#define rs x<<1|1
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
#define root 1,n,1

int sum[M<<2],cov[M<<2];

inline void pushup(int x){sum[x]=sum[ls]+sum[rs];}

inline void pushdown(int x,int m){
    if(cov[x]==-1||m==1) return ;
    cov[ls]=cov[rs]=cov[x];
    sum[ls]=cov[x]*(m-(m>>1));
    sum[rs]=cov[x]*(m>>1);
    cov[x]=-1;
}

void build(int l,int r,int x)
{
    cov[x]=-1;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(lson),build(rson);
}

void update(int L,int R,int k,int l,int r,int x)
{
    if(L<=l&&r<=R){cov[x]=k,sum[x]=(r-l+1)*k;return ;}
    pushdown(x,r-l+1);
    int m=(l+r)>>1;
    if(L<=m) update(L,R,k,lson);
    if(R>m) update(L,R,k,rson);
    pushup(x);
}

int query(int L,int R,int l,int r,int x)
{
    if(L<=l&&r<=R) return sum[x];
    pushdown(x,r-l+1);
    int m=(l+r)>>1,ret=0;
    if(L<=m) ret+=query(L,R,lson);
    if(R>m) ret+=query(L,R,rson);
    return ret;
}

}

namespace hld{

int sz[M],fa[M],dep[M],son[M],top[M];

void predfs(int x)
{
    sz[x]=1;
    for(int i=head[x];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(v==fa[x]) continue;
        fa[v]=x,dep[v]=dep[x]+1;
        predfs(v);sz[x]+=sz[v];
        if(sz[v]>sz[son[x]]) son[x]=v;
    }
}

void dfs(int x,int tp)
{
    dfn[x]=++tim;top[x]=tp;
    if(son[x]) dfs(son[x],tp);
    for(int i=head[x];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(v!=fa[x]&&v!=son[x]) dfs(v,v);
    }
    end[x]=tim;
}

int ask(int x,int y)
{
    int f1=top[x],f2=top[y],sum=0,num=dep[y]-dep[x]+1;//!!!
    while(f1!=f2){
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        sum+=stree::query(dfn[f1],dfn[x],root);
        stree::update(dfn[f1],dfn[x],1,root);
        x=fa[f1],f1=top[x];
    }
    if(dep[x]>dep[y]) swap(x,y);
    sum+=stree::query(dfn[x],dfn[y],root);
    stree::update(dfn[x],dfn[y],1,root);
    return num-sum;
}

}

void work()
{
    char opt[11];int x;
    while(q--){
        scanf("%s%d",opt,&x);x++;
        if(opt[0]=='i') printf("%d\n",hld::ask(1,x));
        if(opt[0]=='u') printf("%d\n",stree::query(dfn[x],end[x],root)),stree::update(dfn[x],end[x],0,root);
    } 
}

void init()
{
    cin>>n;memset(head,-1,sizeof(head));
    for(int i=2,x;i<=n;i++) scanf("%d",&x),x++,add(x,i),add(i,x);
    cin>>q;
    hld::predfs(1);hld::dfs(1,1);
    stree::build(root);
}

int main()
{
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值