[Luogu 2146] NOI2015 软件包管理器

<题目链接>


树剖好题。

通过对题目的分析发现,这些软件构成一棵树,\(0\) 是树根。

每下载一个软件,需要下载根到这个软件的路径上的所有软件;

每卸载一个软件,需要删除这个软件构成的子树上的所有软件。

因此我们可以 HLD,然后用「0/1 线段树」来维护。

最初每一个点的点权都是 \(0\)。

下载 \(x\):根到 \(x\) 的路径上,所有点权改为 \(1\)。

卸载 \(x\):\(x\) 构成的子树上,所有点权改为 \(0\)。

每次操作前后线段树树根的值(即整棵树的和)的绝对值,就是这一次更新的软件数。

为了方便我直接把 \(0 \sim n-1\) 改成 \(1 \sim n\) 去做啦。

就这样,代码如下。

#include <cstdio>
#include <cstring>
const int MAXN=100010,MAXM=200010;
int n,q;
class HLD
{
public:
HLD(int num=0,int cnt=0):num(num),cnt(cnt)
{
memset(vis,0,sizeof vis);
memset(head,0,sizeof head);
}
void Init(void)
{
for(int i=2,x;i<=n;++i)
{
scanf("%d",&x);
AddEdges(i,x+1);
}
DFS1(1,1),DFS2(1,1),SgT.Build(1,1,n);
}
int Install(int i)
{
int ans=0;
ans-=SgT.Sum(),ChangePath(i);
printf("%d ",ans);
return ans+SgT.Sum();
}
int Uninstall(int i)
{
int ans=0;
ans+=SgT.Sum(),ChangeSubtree(i);
printf("%d ",ans);
return ans-SgT.Sum();
}
private:
bool vis[MAXN];
int num,cnt,head[MAXN];
struct node
{
int v,d,ft,top,son,size,DFN;
}s[MAXN];
struct edge
{
int nxt,to;
edge(int nxt=0,int to=0):nxt(nxt),to(to){}
}e[MAXM];
class SegmentTree
{
public:
void Build(int i,int l,int r)
{
s[i]=node(l,r,0);
if(l==r)
{
s[i].v=0;
return;
}
int j=i<<1,mid=l+r>>1;
Build(j,l,mid),Build(j|1,mid+1,r),PushUp(i);
}
void Change(int i,int l,int r,bool v)
{
if(l==s[i].l && r==s[i].r)
{
Modify(i,v);
return;
}
if(s[i].lazy^10)
PushDown(i);
int j=i<<1,mid=s[i].l+s[i].r>>1;
if(r<=mid)
Change(j,l,r,v);
else if(l>mid)
Change(j|1,l,r,v);
else
Change(j,l,mid,v),Change(j|1,mid+1,r,v);
PushUp(i);
}
int Sum(void)
{
return s[1].v;
}
private:
struct node
{
int v,l,r,lazy;
node(int l=0,int r=0,int lazy=0):l(l),r(r),lazy(lazy){}
}s[MAXN<<2];
void Modify(int i,int v)
{
s[i].v=v ? s[i].r-s[i].l+1 : 0;
s[i].lazy=v;
}
void PushUp(int i)
{
int j=i<<1;
s[i].v=s[j].v+s[j|1].v;
}
void PushDown(int i)
{
int j=i<<1;
Modify(j,s[i].lazy),Modify(j|1,s[i].lazy);
s[i].lazy=10;
}
}SgT;
void AddEdge(int u,int v)
{
e[++cnt]=edge(head[u],v);
head[u]=cnt;
}
void AddEdges(int u,int v)
{
AddEdge(u,v),AddEdge(v,u);
}
void DFS1(int u,int k)
{
s[u].d=k,s[u].size=1;
for(int i=head[u],v;i;i=e[i].nxt)
if(!s[v=e[i].to].size)
{
DFS1(v,k+1);
s[v].ft=u,s[u].size+=s[v].size;
if(s[v].size>s[s[u].son].size)
s[u].son=v;
}
}
void DFS2(int u,int top)
{
s[u].top=top,s[u].DFN=++num,vis[u]=1;
if(s[u].son)
DFS2(s[u].son,top);
for(int i=head[u],v;i;i=e[i].nxt)
if(!vis[v=e[i].to])
DFS2(v,v);
}
void ChangePath(int x)
{
int a;
while((a=s[x].top)^1)
SgT.Change(1,s[a].DFN,s[x].DFN,1),x=s[a].ft;
SgT.Change(1,1,s[x].DFN,1);
}
void ChangeSubtree(int x)
{
SgT.Change(1,s[x].DFN,s[x].DFN+s[x].size-1,0);
}
}T;
int main(int argc,char *argv[])
{
scanf("%d",&n);
T.Init();
scanf("%d",&q);
for(int i=1,x;i<=q;++i)
{
char s[20];
scanf("\n%s %d",s,&x);
if(s[0]=='i')
printf("%d\n",T.Install(x+1));
else
printf("%d\n",T.Uninstall(x+1));
}
return 0;
}

谢谢阅读。

[Luogu 2146] NOI2015 软件包管理器的更多相关文章

  1. 【题解】Luogu P2146 [NOI2015]软件包管理器

    题面:https://www.luogu.org/problemnew/lists?name=2146 这道题要用树链剖分,我博客里有对树链剖分的详细介绍 这道题就是树链剖分的模板,详细解释见程序. ...

  2. 【luogu P2146 [NOI2015]软件包管理器】 题解

    题目链接:https://www.luogu.org/problemnew/show/P2146 变量名真毒瘤 我真的再也不把l,left,r,right弄反了 反向思维更好做一些 #include ...

  3. 洛谷 2146 [NOI2015]软件包管理器

    [题解] 每个软件只依赖另一个软件,且依赖关系不构成环,那么很容易想到这是树形结构. 我们用1表示以安装,用0表示未安装或已卸载:那么安装一个软件,就是把它到树根的路径上所有的点都改为1:卸载一个软件 ...

  4. Luogu P2146 [NOI2015]软件包管理器 树剖

    卸载:把子树清空: 安装:把自己到$1$的链改为$1$ #include<cstdio> #include<iostream> #include<cstring> ...

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

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

  6. [BZOJ4196][NOI2015]软件包管理器

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

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

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

  8. [NOI2015]软件包管理器

    4621 [NOI2015]软件包管理器  题目等级 : 钻石 Diamond   题目描述 Description Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过 ...

  9. BZOJ_4196_[Noi2015]软件包管理器_树链剖分

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

随机推荐

  1. js如何使浏览器允许脚本异步加载

    js如何使浏览器允许脚本异步加载 如果脚本体积很大,下载和执行的时间就会很长,因此造成浏览器堵塞,用户会感觉到浏览器“卡死”了,没有任何响应.这显然是很不好的体验,所以浏览器允许脚本异步加载,下面就是 ...

  2. Java学习个人备忘录之多态

    对象的多态性 class 动物 {} class 猫 extends 动物 {} class 狗 extends 动物 {} 猫 x = new 猫();//意思是建立本类的对象 new 猫(),并通 ...

  3. c# 委托初窥

    1.委托可以把方法当作参数在另一个方法中传递和调用 ,委托是方法的快捷方式. 2.委托是一个类. private void BeginSocketThread() { try { IPEndPoint ...

  4. Jenkins系列-Jenkins忘记密码的修复方法

    找回 admin 用户的密码后,可以登录系统修改其他用户的密码. 1. Jenkins 目录结构 Jenkins 没有使用数据库,所有的信息都保存在 JENKINS_HOME 目录下的文件中.其中 J ...

  5. 修改CSV中的某些值

    file.csv文件如下,然后对其中某些值进行变换操作,刚学Powershell的时候操作起来很麻烦,现在看来其实就是对于哈希表的操作. col1,col2,col3,col4 text1,text2 ...

  6. 【bzoj2004】[Hnoi2010]Bus 公交线路 状压dp+矩阵乘法

    题目描述 小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km. 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计 ...

  7. [APIO2015]巴厘岛的雕塑 贪心+DP+特殊数据优化

    写了好久.... 刚刚调了一个小时各种对拍,,,,最后发现是多写了一个等号,,,,内心拒绝 表示一开始看真的是各种懵逼啊 在偷听到某位大佬说的从高位开始贪心后发现可做 首先考虑小数据(因为可以乱搞) ...

  8. 简单谈谈Docker镜像的使用方法_docker

    在上篇文章(在Docker中搭建Nginx服务器)中,我们已经介绍了如何快速地搭建一个实用的Nginx服务器.这次我们将围绕Docker镜像(Docker Image),介绍其使用方法.包括三部分: ...

  9. BZOJ1008:[HNOI2008]越狱——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=1008 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中 ...

  10. GDI & GDI+

    GDI GDI绘图中的映射模式CDC::SetMapMode() GDI编程小结 GDI+ GDI+小例子 关于GDI+ GDI+编程小结