题面

题目链接

P2146 [NOI2015]软件包管理器

题目描述

Linux用户和OSX用户一定对软件包管理器不会陌生。通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置。Debian/Ubuntu使用的apt-get,Fedora/CentOS使用的yum,以及OSX下可用的homebrew都是优秀的软件包管理器。

你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包A依赖软件包B,那么安装软件包A以前,必须先安装软件包B。同时,如果想要卸载软件包B,则必须卸载软件包A。现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除0号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而0号软件包不依赖任何一个软件包。依赖关系不存在环(若有m(m≥2)个软件包A1,A2,A3,⋯,Am,其中A1依赖A2,A2依赖A3,A3依赖A4,……,A[m-1]依赖Am,而Am依赖A1,则称这m个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己。

现在你要为你的软件包管理器写一个依赖解决程序。根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为0。

输入输出格式

输入格式:

从文件manager.in中读入数据。

输入文件的第1行包含1个整数n,表示软件包的总数。软件包从0开始编号。

随后一行包含n−1个整数,相邻整数之间用单个空格隔开,分别表示1,2,3,⋯,n−2,n−1号软件包依赖的软件包的编号。

接下来一行包含1个整数q,表示询问的总数。之后q行,每行1个询问。询问分为两种:

install x:表示安装软件包x

uninstall x:表示卸载软件包x

你需要维护每个软件包的安装状态,一开始所有的软件包都处于未安装状态。

对于每个操作,你需要输出这步操作会改变多少个软件包的安装状态,随后应用这个操作(即改变你维护的安装状态)。

输出格式:

输出到文件manager.out中。

输出文件包括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号软件包会卸载所有的软件包。

【数据范围】

说明

【时空限制】

1000ms,512M

思路

题目好长,先整理下题意。可参考下面的题意理解

给定一棵n个结点的树,根节点为0,每个点的点权为0或1。初始时所有点点权为0,基本操作如下:

install x 查询x的深度与x到根节点路径上点权为0的点的个数,并将这些点的点权全部变为1

uninstall x 查询以x为根结点的子树内所有点中点权为1的点的个数,并将这些的点的点权全部变为0

为什么可以这样转化呢?

首先每个点的点权代表是否被安装(其中1代表安装)。

安装x时,需要查询x到0路径上有几个没有被安装,即有几个为0。x到根节点的结点个数即x的深度,其中1的个数即为x到根节点路径上的点的点权和,作差可得其中0的个数,即未安装的个数。然后再将这些点均标记为已安装,即标为1。

卸载x时,由于依赖x的安装包都要被卸载,即以x为根节点的子树内所有点都要变为0。查询时只需要查询这些点中有多少个点权为1,即这些点的点权和。最后将他们全部变为0。

这样,就基本是一道树剖板子题了吧。不过,在线段树上还要略做改变

在下传时,+=可以改为=。并且,懒标记如果直接打上0和1,那么下传时不清楚这是由于本身就是0还是懒标记打上了0,所以我定义懒标记是1和2,不用下传时懒标记是0(说不明白了,看代码吧)

AC代码

#include<bits/stdc++.h>
const int maxn=100010;
using namespace std; int n,m;
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int fa[maxn],son[maxn],dep[maxn],len[maxn];
int cnt,top[maxn],nid[maxn],nw[maxn];
struct SegmentTree
{
int l,r,sum,tag;
#define l(a) tree[a].l
#define r(a) tree[a].r
#define m(a) ((l(a)+r(a))>>1)
#define len(a) (r(a)-l(a)+1)
#define s(a) tree[a].sum
#define t(a) tree[a].tag
}tree[maxn<<2]; void dfs1(int u,int f,int d)
{
dep[u]=d;fa[u]=f;len[u]=1;
int maxson=-1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==f) continue;
dfs1(v,u,d+1);
len[u]+=len[v];
if(len[v]>maxson) maxson=len[v],son[u]=v;
}
} void dfs2(int p,int t)
{
nid[p]=++cnt;
top[p]=t;
if(!son[p]) return;
dfs2(son[p],t);
for(int i=head[p];i;i=nxt[i])
{
int v=to[i];
if(v==fa[p] || v==son[p]) continue;
dfs2(v,v);
}
} void BuildTree(int p,int l,int r)
{
l(p)=l;r(p)=r;
if(l==r) return;
BuildTree(p<<1,l,m(p));
BuildTree(p<<1|1,m(p)+1,r);
} void PushDown(int p)
{
if(t(p)==1)
{
s(p<<1)=len(p<<1);s(p<<1|1)=len(p<<1|1);
t(p<<1)=t(p<<1|1)=1;
t(p)=0;
}
else if(t(p)==2)
{
s(p<<1)=0;s(p<<1|1)=0;
t(p<<1)=t(p<<1|1)=2;
t(p)=0;
}
} void Change1(int p,int l,int r,int k)
{
if(l<=l(p) && r>=r(p))
{
if(k==1) s(p)=len(p);
else if(k==2) s(p)=0;
t(p)=k;
return;
}
PushDown(p);
if(l<=m(p)) Change1(p<<1,l,r,k);
if(r>m(p)) Change1(p<<1|1,l,r,k);
s(p)=s(p<<1)+s(p<<1|1);
} int Ask1(int p,int l,int r)
{
if(l<=l(p) && r>=r(p)) return s(p);
PushDown(p);
int ans=0;
if(l<=m(p)) ans+=Ask1(p<<1,l,r);
if(r>m(p)) ans+=Ask1(p<<1|1,l,r);
return ans;
} void Change2(int p)
{
while(top[p]!=1)
{
Change1(1,nid[top[p]],nid[p],1);
p=fa[top[p]];
}
Change1(1,1,nid[p],1);
} int Ask2(int p)
{
int ans=0;
while(top[p]!=1)
{
ans+=Ask1(1,nid[top[p]],nid[p]);
p=fa[top[p]];
}
ans+=Ask1(1,1,nid[p]);
return ans;
} int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int v;scanf("%d",&v);
to[++tot]=v+1;nxt[tot]=head[i+1];head[i+1]=tot;
to[++tot]=i+1;nxt[tot]=head[v+1];head[v+1]=tot;
}
dfs1(1,1,1);
dfs2(1,1);
BuildTree(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
string way;
cin>>way;
if(way=="install")
{
int p;scanf("%d",&p);p++;
printf("%d\n",dep[p]-Ask2(p));
Change2(p);
}
if(way=="uninstall")
{
int p;scanf("%d",&p);p++;
printf("%d\n",Ask1(1,nid[p],nid[p]+len[p]-1));
Change1(1,nid[p],nid[p]+len[p]-1,2);
}
}
return 0;
}

总结

主要是要读清楚题,将其本质剖析出来,想明白涉及了哪些修改和查询方式

洛谷 P2146 [NOI2015]软件包管理器 树链剖分的更多相关文章

  1. 【BZOJ4196】[Noi2015]软件包管理器 树链剖分

    [Noi2015]软件包管理器 树链剖分 Description Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从 ...

  2. 洛谷 P2146 [NOI2015]软件包管理器 解题报告

    P2146 [NOI2015]软件包管理器 题目描述 Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软 ...

  3. 洛谷P2146 [NOI2015]软件包管理器 题解 树链剖分+线段树

    题目链接:https://www.luogu.org/problem/P2146 本题涉及算法: 树链剖分: 线段树(区间更新及求和,涉及懒惰标记) 然后对于每次 install x ,需要将 x 到 ...

  4. BZOJ 4196: [Noi2015]软件包管理器 [树链剖分 DFS序]

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1352  Solved: 780[Submit][Stat ...

  5. Bzoj 4196: [Noi2015]软件包管理器 树链剖分

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 721  Solved: 419[Submit][Statu ...

  6. bzoj 4196 [Noi2015]软件包管理器 (树链剖分+线段树)

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2852  Solved: 1668[Submit][Sta ...

  7. [BZOJ4196][NOI2015]软件包管理器(树链剖分)

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2166  Solved: 1253[Submit][Sta ...

  8. 洛谷 P2146 [NOI2015]软件包管理器 (树链剖分模板题)

    题目描述 Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个 ...

  9. 洛谷P2146 [NOI2015]软件包管理器

    https://www.luogu.org/problemnew/show/P2146 传送门 简单的树链剖分......维护下当前安装了多少个包......修改后查询下就行了......附上极其丑陋 ...

随机推荐

  1. Java内功修炼系列一拦截器

    在动态代理中,我们知道在代理类中,执行真实对象的方法前后可以增加一些其他的逻辑,这些逻辑并不是真实对象能够实现的方法,比如一个租房的用户希望租一套公寓,但是中介所代理的这个房东并没有可以出租的公寓,那 ...

  2. 隐藏和显示<td>

    <td id="ifXc"><input type="text" value="1"></td>隐藏$( ...

  3. 观察者模式(Observer、Subject、ConcreteSubject、ConcreteObserver)(监护、订阅)

    建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应. 在此发生改变的对象称之为观察目标(被观察者),而被通知的对象称为观察者,一个观察者目标可以对应多个观 ...

  4. 享元模式(Flyweight、FlyweightFactory)(围棋棋子共享)

    (使用共享对象可有效地支持大量的细粒度的对象.) 假设开发一个围棋程序,围棋程序的围棋的棋子包含了颜色.大小.位置等信息.在定义一个棋盘容器来存放这些棋子. 我们可以发现,棋盘的成员变量包含了一个棋子 ...

  5. C#获取C# DLL中的指定接口的所有实现实例 - qq_19759475的博客 - CSDN博客

    原文:C#获取C# DLL中的指定接口的所有实现实例 - qq_19759475的博客 - CSDN博客 public static List<T> CreateTarInterface& ...

  6. LINUX对超级用户和普通用户的理解

    什么是超级用户 在所有Linux系统中,系统都是通过UID来区分用户权限级别的,而UID为0的用户被系统约定为是具有超级权限.超级用户具有在系统约定的最高权限满园内操作,所以说超级用户可以完成系统管理 ...

  7. UVA11722 Jonining with Friend

    Joining with Friend You are going from Dhaka to Chittagong by train and you came to know one of your ...

  8. nslookup获取域名对应的的ip地址

    1.先用nslookup获得域名对应的主机ip:nslookup 域名 2.再根据主机ip获得对应的主机名称: host ip $ nslookup baidu.com Server: 127.0.1 ...

  9. SSM11-solr服务的搭建

    1.  Solr服务搭建 1.1. Solr的环境 Solr是java开发. 需要安装jdk. 安装环境Linux. 需要安装Tomcat. 1.2. 搭建步骤 第一步:把solr 的压缩包上传到Li ...

  10. mybatis深入理解(七)-----MyBatis缓存机制的设计与实现

    缓存设计 MyBatis将数据缓存设计成两级结构,分为一级缓存.二级缓存: 一级缓存是Session会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中,又被称之为本地缓存.一级缓存是 ...