洛谷P2146 软件包管理器

题目

题目描述

Linux用户和OS X用户一定对软件包管理器不会陌生
通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置
Debian/Ubuntu使用的apt-getFedora/CentOS使用的yum,以及OS X下可用的homebrew都是优秀的软件包管理器
你决定设计你自己的软件包管理器
不可避免地,你要解决软件包之间的依赖问题
如果软件包A依赖软件包B,那么安装软件包A以前,必须先安装软件包B
同时,如果想要卸载软件包B,则必须卸载软件包A
现在你已经获得了所有的软件包之间的依赖关系,而且,由于你之前的工作,除0号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而0号软件包不依赖任何一个软件包
依赖关系不存在环(若有$m(m\geqslant 2)$个软件包$A_1,A_2,A_3,\cdots \cdots,A_m$,其中$A_1$依赖$A_2$,$A_2$依赖$A_3$,$A_3$依赖$A_4$,……,$A_{m−1}$依赖$A_m$,而$A_m$依赖$A_1$,则称这$m$个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己
现在你要为你的软件包管理器写一个依赖解决程序
根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分
注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为$0$

输入格式

输入文件的第$1$行包含$1$个正整数$n$,表示软件包的总数,软件包从$0$开始编号
随后一行包含$n−1$个整数,相邻整数之间用单个空格隔开,分别表示$1,2,3,\cdots \cdots,n−2,n−1$号软件包依赖的软件包的编号
接下来一行包含$1$个正整数$q$,表示询问的总数
之后$q$行,每行$1$个询问
询问分为两种:
install x:表示安装软件包$x$
uninstall x:表示卸载软件包$x$
你需要维护每个软件包的安装状态,一开始所有的软件包都处于未安装状态
对于每个操作,你需要输出这步操作会改变多少个软件包的安装状态,随后应用这个操作(即改变你维护的安装状态)

输出格式

输出文件包括$q$行
输出文件的第$i$行输出$1$个整数,为第$i$步操作中改变安装状态的软件包数

样例

样例输入1

7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0

样例输出1

3
1
3
2
3

样例输入2

10
0 1 2 1 3 0 0 3 2
10
install 0
install 3
uninstall 2
install 7
install 5
install 9
uninstall 9
install 4
install 1
install 9

样例输出2

1
3
2
1
3
1
1
1
0
1

数据范围与提示

样例1说明

在这里插入图片描述
一开始所有的软件包都处于未安装状态
安装$5$号软件包,需要安装$0,1,5$三个软件包
之后安装$6$号软件包,只需要安装$6$号软件包
此时安装了$0,1,5,6$四个软件包
卸载 1 号软件包需要卸载$1,5,6$三个软件包
此时只有$0$号软件包还处于安装状态
之后安装$4$号软件包,需要安装$1,4$两个软件包
此时$0,1,4$处在安装状态
最后,卸载$0$号软件包会卸载所有的软件包

数据范围

在这里插入图片描述

题解

树剖裸题……
建线段树记录相应一段DFS序中安装软件的数量
安装软件的时候就直接把这个软件到根节点的路径上的所有点的权值全部修改为$1$
卸载软件的时候就直接把这个软件的所有子树上的所有点的权值全部修改为$0$就可以了
剩下的细节我就不说了,反正是个很裸的题
附上代码:

#include<cstdio>
#define l(x) t[x].l
#define r(x) t[x].r
#define s(x) t[x].sum
#define a(x) t[x].add
int n,q,tot,cnt,head[100010],to[100010],nxt[100010],fa[100010],size[100010],dep[100010],big[100010];
int top[100010],dfn[100010],dfn2[100010],sor[100010];
char op[20];
struct ppap
{
    int l,r,sum,add;
}t[400010];
void add(int u,int v)
{
    nxt[++tot]=head[u],head[u]=tot,to[tot]=v;
}
void dfs1(int x,int deep)
{
    dep[x]=deep,size[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        dfs1(to[i],deep+1),size[x]+=size[to[i]];
        if(size[to[i]]>size[big[x]]) big[x]=to[i];
    }
}
void dfs2(int x,int Top)
{
    top[x]=Top,dfn[x]=++cnt,sor[cnt]=x;
    if(big[x]==100001){dfn2[x]=cnt;return;}
    dfs2(big[x],Top);
    for(int i=head[x];i;i=nxt[i]) if(to[i]!=big[x]) dfs2(to[i],to[i]);
    dfn2[x]=cnt;
}
void spread(int p)
{
    if(a(p)){
        a(p*2)=a(p*2+1)=a(p);
        if(a(p)==1) s(p*2)=r(p*2)-l(p*2)+1,s(p*2+1)=r(p*2+1)-l(p*2+1)+1;
        if(a(p)==2) s(p*2)=s(p*2+1)=0;
        a(p)=0;
    }
}
void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
}
int ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)) return s(p);
    spread(p);
    int mid=(l(p)+r(p))>>1,ans=0;
    if(l<=mid) ans+=ask(p*2,l,r);
    if(r>mid) ans+=ask(p*2+1,l,r);
    return ans;
}
void change(int p,int l,int r,int x)
{
    if(l<=l(p)&&r>=r(p)){
        if(x) a(p)=1,s(p)=r(p)-l(p)+1;
        else a(p)=2,s(p)=0;
        return;
    }
    spread(p);
    int mid=(l(p)+r(p))>>1;
    if(l<=mid) change(p*2,l,r,x);
    if(r>mid) change(p*2+1,l,r,x);
    s(p)=s(p*2)+s(p*2+1);
}
int Install(int x)
{
    int ans=dep[x],u=x;
    while(top[u]) ans-=ask(1,dfn[top[u]],dfn[u]),change(1,dfn[top[u]],dfn[u],1),u=fa[top[u]];
    ans-=ask(1,dfn[0],dfn[u]),change(1,dfn[0],dfn[u],1);
    return ans;
}
int Uninstall(int x)
{
    int ans;
    ans=ask(1,dfn[x],dfn2[x]),change(1,dfn[x],dfn2[x],0);
    return ans;
}
int main()
{
    freopen("manager.in","r",stdin);
    freopen("manager.out","w",stdout);
    scanf("%d",&n),big[0]=100001;
    for(int i=1,x;i<n;i++) scanf("%d",&x),add(x,i),fa[i]=x,big[i]=100001;
    dfs1(0,1),dfs2(0,0),build(1,1,n);
    scanf("%d",&q);
    for(int i=1,x;i<=q;i++){
        scanf("%s %d",op,&x);
        if(op[0]=='i') printf("%d\n",Install(x));
        else printf("%d\n",Uninstall(x));
    }
}

喜欢的话,给博主赏一杯冰阔乐吧~


 上一篇
微积分基础之图形面积(体积)计算 微积分基础之图形面积(体积)计算
一、平面图形面积$$\boxed{积分的要领1:以长方形为基础来思考}$$ 1、简单图形的面积(1)长方形长$\times$宽,不会的请离开 (2)三角形底$\times$高/2,不会的请离开 (3)平行四边形底$\times$高,不会
2020-05-29
下一篇 
洛谷P4072 征途 洛谷P4072 征途
题目题目描述原题 Pine开始了从$S$地到$T$地的征途从$S$地到$T$地的路可以划分成$n$段,相邻两段路的分界点设有休息站Pine计划用$m$天到达$T$地。除第$m$天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在
2020-05-27
  目录