题面
【题目描述】
【输入】
【输出】
输出文件包括
q
q
q行。 输出文件的第
i
i
i行输出
1
1
1 个整数,为第i 步操作中改变安装状态的软件包数。
【样例输入】
7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0
【样例输出】
3
1
3
2
3
算法分析
述链剖分模板题目。
题目文字较多,结合样例很容易理解。有两种操作:
- 安装第 x x x号软件包,等效于将根节点到 x x x节点都进行安装,即树上路径修改。
- 卸载第
x
x
x号软件包,等效于将
x
x
x为根的子树全部卸载,即树上子树修改。
使用线段树维护。
将软件安装设为 1 1 1,未安装设为 0 。 线 段 树 中 , 定 义 0。线段树中,定义 0。线段树中,定义sum 表 示 节 点 对 于 区 间 的 和 , 那 么 安 装 的 软 件 总 数 即 为 线 段 树 根 节 点 的 值 表示节点对于区间的和,那么安装的软件总数即为线段树根节点的值 表示节点对于区间的和,那么安装的软件总数即为线段树根节点的值sum[1]$。( 1 1 1为线段树根节点)
答案就是每次操作后,安装的软件总数与上一次的差的绝对值。
参考程序
#include<bits/stdc++.h>
#define N 100100
using namespace std;
int head[N],nxt[N],to[N],tot;
int n,m;
int father[N],deep[N],size[N];//父节点,深度,子树结点数
int son[N],top[N];//重儿子,所在重路径的顶部结点(深度最小的结点)
int id[N],rev[N],t;//在线段树中的下标(dfs序),线段树中下标的结点,即rev[id[u]]=u ,dfs序号
int sum[4*N]; //线段树
int lazy[4*N];//lazy=1为安装,lazy=0表示卸载,lazy=-1为初始值,表示没有操作
void Add(int u,int v)
{
nxt[++tot]=head[u];
to[tot]=v;
head[u]=tot;
}
void dfs1(int u,int dad)
{
size[u]=1; //本身结点数为1
father[u]=dad;
deep[u]=deep[dad]+1;
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(v==dad) continue;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;//找重儿子
}
}
void dfs2(int u,int dad)
{
int v=son[u];
if(v) //优先选择重儿子
{
id[v]=++t; //dfs序列
top[v]=top[u];
rev[t]=v;
dfs2(v,u);
}
for(int i=head[u];i!=-1;i=nxt[i])
{
v=to[i];
if(!top[v])
{
id[v]=++t;
top[v]=v;
rev[t]=v;
dfs2(v,u);
}
}
}
/*void built(int k,int l,int r) //线段树建树
{
if(l==r) return; //初始化
int mid=(l+r)/2;
built(2*k,l,mid);
built(2*k+1,mid+1,r);
sum[k]=sum[2*k]+sum[2*k+1];
} */
void pushdown(int k,int l,int r) //标记下传
{
if(lazy[k]==-1) return;
int mid=(l+r)/2;
lazy[2*k]=lazy[2*k+1]=lazy[k];//全部安装或全部卸载
sum[2*k]=(mid-l+1)*lazy[k];
sum[2*k+1]=(r-mid)*lazy[k];
lazy[k]=-1;
}
void update(int k,int l,int r,int x,int y,int v) //区间修改
{
if(x>r||y<l) return;
if(x<=l&&r<=y)
{
lazy[k]=v;
sum[k]=(r-l+1)*v;
return;
}
pushdown(k,l,r); //下传标记
int mid=(l+r)/2;
if(x<=mid) update(2*k,l,mid,x,y,v);
if(y>=mid+1) update(2*k+1,mid+1,r,x,y,v);
sum[k]=sum[2*k]+sum[2*k+1]; //下传后更新
}
void change(int u,int v)
{
int fu=top[u],fv=top[v];
while(fu!=fv) //不在同一条重链上
{
if(deep[fu]<deep[fv]) {swap(u,v);swap(fu,fv);} //选择深度大的往上跳
update(1,1,n,id[fu],id[u],1); //路径fu->u节点全部赋值为1,表示安装
u=father[fu];
fu=top[u];
}
if(deep[u]>deep[v]) swap(u,v); //已经跳到同一条重路径上了
update(1,1,n,id[u],id[v],1); //路径u->v节点全部赋值为1,表示安装
}
int main()
{
memset(head,-1,sizeof(head));
memset(lazy,-1,sizeof(lazy));
scanf("%d",&n);
int x;
for(int i=2;i<=n;i++) //节点从0~n-1,更改为1~n,方便处理
{
scanf("%d",&x);
x++;
Add(x,i); //方向是单向的
}
scanf("%d",&m);
dfs1(1,0);
id[1]=++t; //初始化根节点
top[1]=1;
rev[1]=1;
dfs2(1,0);
//built(1,1,n); //初始值全为0,可以不需要建树
char s[15];
int temp=0;
for(int i=1;i<=m;i++)
{
scanf("%s%d",s,&x);x++;
if(s[0]=='i') change(1,x); //路径修改
else update(1,1,n,id[x],id[x]+size[x]-1,0); //子树修改,全部卸载,标记为0
printf("%d\n",abs(temp-sum[1])); //修改的值就是和上一次安装的总数之差的绝对值
temp=sum[1];
}
return 0;
}