题目描述

题面比较啰唆,我先把大体意思讲一下:

首先,有编号从\(0\)到\(N-1\)的\(N\)个节点,根节点一定是\(0\)号节点(无前驱)

(我把下标都加上了一,转化为以\(1\)为起始下标的点集,那么根节点编号为\(1\),注意一下)

输入会给定根节点以外的节点的前驱,即父节点编号。

还有\(M\)次操作:

  • \(install:\) 根据题意也就是将给定的节点\(x\)到根的路径上的每一个节点的权值赋值为\(1\)
  • \(uninstall:\) 根据题意也就是将以给定的节点\(x\)为根的子树的每一个节点的权值赋值为\(0\)

看到这里就很显然,这是一道树链剖分的模板题。


基本思路 (树链剖分+线段树)

既然是模板题,思路就很简单了,树剖之后加线段树维护即可。(不过还是有点坑点的...)


细节注意事项

接下来就是关于这道题的几个坑点。

坑点一:点下标出锅

这里我之前也有提到,尤其是在输入时,下面我把两种输入方式的正确写法都说一下:

以\(0\)为下标,这意味着你的输入是从下标\(1\)到\(N-1\)的,所以要这样写:

    for(rg int x,i=1;i<=n-1;i++) x=read();//这里主要只说下标处理,连边什么的见详细代码

以\(1\)为下标则是:

    for(rg int x,i=2;i<=n;i++) x=read();

这里我有一点检验方法,还是比较实用的,毕竟下标的处理是很基本而又重要的:

  1. 试着通过你的for循环算一下你的循环次数
  2. 确保你的循环变量\(i\)(或其他变量名)的循环起点

这样的话有可以适当避免下标出锅问题(我就是因为下标问题卡了十多分钟,泪的教训啊\(qwq\))

坑点二:线段树修改子节点信息(标记下传)出锅

在此篇题解的开始我便用粗体强调了,这里再说一次:

每次操作是在赋值,也就是覆盖之前的信息(这也正是选择线段树来维护而不是分块等数据结构的理由)

具体代码实现可以看一下我写的:

    inline void f(int rt,int l,int r,int v){
//rt为当前接受信息的线段树节点编号
//l为该节点包含区间的左端点,r为右端点
//v为父节点的lazy tag值
tag[rt]=v,sum[rt]=v*(r-l+1);//该题正确写法
/*tag[rt]+=v,sum[rt]+=v*(r-l+1)*/
//一般写法,区别就在于+=和=,小小的=就帮我们实现了覆盖操作
}
inline void pushdown(int rt,int l,int r,int mid){
//由于涉及到赋为0的操作,所以我用了-1表示lazy tag为空的状态
if(tag[rt]!=-1){
f(lc(rt),l,mid,tag[rt);
f(rc(rt),mid+1,r,tag[rt]);
tag[rt]=-1;
}
}

坑点三:输出出锅

其实这一点还是比较好处理的,不过我第一次还是没有直接写对(还是涉及到读题的问题)

题目是这样说的:

输出文件的第\(i\)行输出\(1\)个整数,为第\(i\)步操作中改变安装状态的软件包数。

也就是说我们每次输出的是变化量而并非操作后的总数,具体实现的话只需要在每次操作前事先记录一下总量\(t_1\),再记录一下每次操作完后的新的总量\(t_2\),输出\(\vert t_1-t_2 \vert\)即可(注意换行...)

参考代码

下面贴上蒟蒻的代码。。。

#include<cstdio>
#include<algorithm>
#define rg register
const int MAXN=100010;
using namespace std;
inline int read(){
int s=0;bool f=false;char c=getchar();
while(c<'0'||c>'9')f|=(c=='-'),c=getchar();
while(c>='0'&&c<='9')s=(s<<3)+(s<<1)+(c^48),c=getchar();
return (f)?(-s):(s);
}
int n,m;
int tot,head[MAXN],nxt[MAXN],ver[MAXN];
inline void Add_edge(int u,int v){
nxt[++tot]=head[u],head[u]=tot,ver[tot]=v;
}
int sum[MAXN<<2],tag[MAXN<<2];
inline int lc(int rt){return rt<<1;}
inline int rc(int rt){return rt<<1|1;}
inline void pushup(int rt){
sum[rt]=sum[lc(rt)]+sum[rc(rt)];
}
inline void f(int rt,int l,int r,int v){
tag[rt]=v,sum[rt]=v*(r-l+1);
}
inline void pushdown(int rt,int l,int r,int mid){
if(tag[rt]!=-1){
f(lc(rt),l,mid,tag[rt]);
f(rc(rt),mid+1,r,tag[rt]);
tag[rt]=-1;
}
}
inline void update(int rt,int l,int r,int x,int y,int v){
if(l>y||r<x) return;
if(x<=l&&r<=y) return f(rt,l,r,v);
int mid=(l+r)>>1;
pushdown(rt,l,r,mid);
update(lc(rt),l,mid,x,y,v);
update(rc(rt),mid+1,r,x,y,v);
pushup(rt);
}
inline int query(int rt,int l,int r,int x,int y){
if(l>y||r<x) return 0;
if(x<=l&&r<=y) return sum[rt];
int mid=(l+r)>>1;
pushdown(rt,l,r,mid);
return query(lc(rt),l,mid,x,y)+query(rc(rt),mid+1,r,x,y);
}
int top[MAXN],seg[MAXN];
int dep[MAXN],siz[MAXN],son[MAXN],father[MAXN];
inline void dfs1(int u,int fa){
siz[u]=1;
father[u]=fa;
dep[u]=dep[fa]+1;
for(rg int v,i=head[u];i;i=nxt[i])
if(!dep[v=ver[i]]){
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
inline void dfs2(int u,int topf){
top[u]=topf;
seg[u]=++seg[0];
if(!son[u]) return;
dfs2(son[u],topf);
for(rg int v,i=head[u];i;i=nxt[i])
if(!top[v=ver[i]]) dfs2(v,v);
}
inline void uptRange(int x,int y,int v){
int fx=top[x],fy=top[y];
while(fx!=fy){
if(dep[fx]>dep[fy]){
update(1,1,n,seg[fx],seg[x],v);
x=father[fx],fx=top[x];
}
else{
update(1,1,n,seg[fy],seg[y],v);
y=father[fy],fy=top[y];
}
}
if(dep[x]<dep[y])
update(1,1,n,seg[x],seg[y],v);
else
update(1,1,n,seg[y],seg[x],v);
}
inline void uptSon(int x,int v){
update(1,1,n,seg[x],seg[x]+siz[x]-1,v);
}
int main(){
n=read();
for(rg int fa,i=2;i<=n;i++)
fa=read()+1,Add_edge(fa,i);
fill(sum+1,sum+n+1,0);
fill(tag+1,tag+n+1,-1);
//fill 这个函数是algorithm库里的一个函数,用法与sort类似,用于实现数组的初始化
//之所以没写memset是因为一开始以为不能用memset初始化负数,不过好像可以?
dfs1(1,0);
dfs2(1,1);
m=read();
char s[20];
for(rg int i=1;i<=m;i++){
scanf("%s",s);
int x=read()+1;
int t1=sum[1];
if(s[0]=='i'){
uptRange(x,1,1);
printf("%d\n",abs(t1-sum[1]));
}
else{
uptSon(x,0);
printf("%d\n",abs(t1-sum[1]));
}
}
return 0;
}

完结撒花\(qwq\)

「NOI2015」软件包管理器的更多相关文章

  1. 【LOJ】 #2130. 「NOI2015」软件包管理器

    题解 连树剖我都写跪一次,我现在怎么那么老年啊= = 简直滚粗预定了啊.. 我们线段树维护树剖只需要资瓷区间覆盖和区间求和就好了 安装的时候看看自己到根有多少包装了,dep减去这个数量就好 卸载的时候 ...

  2. LibreOJ #2130. 「NOI2015」软件包管理器

    内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 上传者: 匿名 树链剖分+线段树 屠龙宝刀点击就送 #include <vector> ...

  3. 「NOI2015」「Codevs4621」软件包管理器(树链剖分

    4621 [NOI2015]软件包管理器 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond   题目描述 Description Linux用户和OSX用户一定对 ...

  4. 【BZOJ4196】【NOI2015】软件包管理器(树链剖分,线段树)

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

  5. BZOJ_4196_[NOI2015]_软件包管理器_(树链剖分)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=4196 给出一棵树,树上点权为0或1.u权值为1的条件是从根节点到u路径上的所有点权值都为1.u ...

  6. 【NOI2015】 软件包管理器 - 树链剖分

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

  7. 【NOI2015】软件包管理器

    NOI难得的水题,话说还是T2诶……又学到了线段树的一种新的魔性使用 看sxysxy大神的代码才写出来的,sxysxy_orz 原题: Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包 ...

  8. NOI2015 D1T2 软件包管理器

    题目传送门; 这个貌似是我这个蒟蒻做的第一道NOI系列的题了吧...这题的算法是树链剖分,其实基本上就是很常见的树剖+线段树,题目既然是要求每次安装或卸载改变的软件包的数目,那么就在每次操作前记录下线 ...

  9. 【BZOJ4196】【Noi2015】软件包管理器

    原题传送门 题意: 给你一棵树,有2种操作: 1.使得某个点到根节点路径上的所有点权值赋为1. 2.使得某节点的子树中所有节点权值赋为0. 每次操作要求输出权值更改的节点个数. 解题思路: 显然是用树 ...

随机推荐

  1. linux下的apue.3e安装[Unix环境高级编程]

    近期正在看<Unix环境高级编程>一书,目前看了20多页,书中 有几个c的例程,于是想着,也让它运行一下,看看是否跟描述相符,做开发人员本应该这样,于是敲了一部分代码,然后编译,发现并不能 ...

  2. Jekyll本地搭建开发环境以及Github部署流程

    转载自: http://www.jianshu.com/p/f37a96f83d51 前言 博客从wordpres迁移到Jekyll上来了,整个过程还是很顺利的.Jekyll是什么?它是一个简单静态博 ...

  3. React项目中遇到的那些坑

    1.react中路由跳转后页面不置顶问题 问题: 从页面A跳转到页面B,页面A滚动到中间位置,跳转后页面B也会在中间位置 解决方法:在顶部组件的生命周期中进行判断,例如 componentWillRe ...

  4. (二)tensorflow-gpu2.0之自动导数

    import tensorflow as tf ''' 梯度:导数或偏导数 1.在什么点的导数:在点(a,b,c,w)=(1,2,3,4)点的导数 2.梯度环境 对谁求导: 对w求导 函数: y = ...

  5. VMwarePro密钥

    VMware10Pro密钥 5F29M-48312-8ZDF9-A8A5K-2AM0Z VMware12Pro密钥 5A02H-AU243-TZJ49-GTC7K-3C61N VF5XA-FNDDJ- ...

  6. 牛客跨年AK场-小sum的假期安排

    链接:https://ac.nowcoder.com/acm/contest/3800/G来源:牛客网 题目描述 小 sun 非常喜欢放假,尤其是那种连在一起的长假,在放假的时候小 sun 会感到快乐 ...

  7. 使用 OClint 进行静态代码分析

    OCLint 就是一个建立在 Clang 上的工具,能够发现代码中潜在的问题. 最近需要一个静态分析代码工具,帮助我们发布运行应用前找到代码潜在的问题. 其实对于iOS开发,我们的日常开发上已经用到了 ...

  8. 「JSOI2015」最大公约数

    「JSOI2015」最大公约数 传送门 考虑先枚举区间左端点, 然后我们会发现所有可能的区间虽然有 \(O(n)\) 个,但是本质不同的区间 \(\gcd\) 只有 \(\log n\) 级别,而且是 ...

  9. 牛客网Sql

    牛客网Sql: 1.查询最晚入职的员工信息  select * from employees where hire_date =(select max(hire_date) from employee ...

  10. 虚拟机与ubuntu系统的安装与基础操作

    1.虚拟机的下载: 常见的虚拟机软件有:VMware  VirtuaIBOX  Virtual PC  等. 这里主要介绍VMware ,VMware目前已经有很多个版本,可以根据个人情况进行选择.安 ...