【NOI2015T2】软件包管理器-树链剖分维护路径和子树信息
测试地址:软件包管理器
做法:根据条件构图,可以看出原图只有N-1条边,而且不存在环,推断出原图是一棵树。如果把一个软件包依赖的软件包看做它的父亲,那么这就是一棵以0为根的有根树。然后我们再分析操作如何处理。设每个点的点权为0或1,为0表示未安装,为1表示已安装。对于install操作,我们可以看做询问从询问点到根路径上的点数减去路径上点权之和,然后修改从询问点到根的路径上所有点权为1。对于uninstall操作,我们可以看做询问以询问点为根的子树上的点权之和,然后修改以询问点为根的子树上的所有点权为0。维护树上的路径信息我们显然可以用树链剖分来做,问题是维护子树的信息。我们回顾一下树链剖分往线段树中加点的过程,发现整个过程就是一个DFS,所以我们知道线段树中点的排列是按DFS序的。所以我们可以记录进入和离开每个点时的时刻,这两个时刻中间的点就是以这个点为根的子树上的点,是一个连续的区间,很方便用线段树维护。树链剖分的复杂度是O(nlogn),每个install操作复杂度为O(log^2 n),每个uninstall操作复杂度为O(logn),所以总复杂度为O(nlogn+qlog^2 n),可以通过全部数据。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,q,start[100010],end[100010],first[100010]={0},tot=0,t;
int son[100010],siz[100010],dep[100010],fa[100010],top[100010],h[100010];
char op[30];
struct edge {int v,next;} e[200010];
struct segnode
{
int p,sum;
}seg[300010];
void insert(int a,int b)
{
e[++tot].v=b,e[tot].next=first[a],first[a]=tot;
}
void buildtree(int no,int l,int r)
{
seg[no].p=-1;seg[no].sum=0;
if (l==r) return;
int mid=(l+r)>>1;
buildtree(no<<1,l,mid);
buildtree(no<<1|1,mid+1,r);
}
void pushdown(int no,int l,int r)
{
int mid=(l+r)>>1;
if (seg[no].p!=-1)
{
seg[no<<1].p=seg[no<<1|1].p=seg[no].p;
if (seg[no].p)
{
seg[no<<1].sum=mid-l+1;
seg[no<<1|1].sum=r-mid;
}
else seg[no<<1].sum=seg[no<<1|1].sum=0;
seg[no].p=-1;
}
}
void pushup(int no)
{
seg[no].sum=seg[no<<1].sum+seg[no<<1|1].sum;
}
void modify(int no,int l,int r,int s,int t,int val)
{
if (l>=s&&r<=t)
{
seg[no].p=val;
if (seg[no].p) seg[no].sum=r-l+1;
else seg[no].sum=0;
return;
}
int mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) modify(no<<1,l,mid,s,t,val);
if (t>mid) modify(no<<1|1,mid+1,r,s,t,val);
pushup(no);
}
int query(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t) return seg[no].sum;
int mid=(l+r)>>1,ss=0;
pushdown(no,l,r);
if (s<=mid) ss+=query(no<<1,l,mid,s,t);
if (t>mid) ss+=query(no<<1|1,mid+1,r,s,t);
return ss;
}
//以上是线段树部分
void dfs1(int v)
{
son[v]=-1,siz[v]=1;
for(int i=first[v];i;i=e[i].next)
{
dep[e[i].v]=dep[v]+1;
fa[e[i].v]=v;
dfs1(e[i].v);
if (son[v]==-1||siz[e[i].v]>siz[son[v]]) son[v]=e[i].v;
siz[v]+=siz[e[i].v];
}
}
void dfs2(int v,int tp)
{
top[v]=tp;h[v]=++tot;
start[v]=tot;
if (son[v]!=-1) dfs2(son[v],tp);
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=son[v]) dfs2(e[i].v,e[i].v);
end[v]=tot;
}
void treemodify(int x,int y,int val)
{
while(top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
modify(1,1,n,h[top[x]],h[x],val);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
modify(1,1,n,h[x],h[y],val);
}
int treequery(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=query(1,1,n,h[top[x]],h[x]);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
ans+=query(1,1,n,h[x],h[y]);
return ans;
}
//以上是树链剖分部分
int main()
{
scanf("%d",&n);
for(int i=1,a;i<n;i++)
{
scanf("%d",&a);
insert(a,i);
}
fa[0]=-1;dep[0]=0;
dfs1(0);
tot=0;t=1;
dfs2(0,0);
buildtree(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",dep[x]+1-treequery(0,x));
treemodify(0,x,1);
}
if (op[0]=='u')
{
printf("%d\n",query(1,1,n,start[x],end[x]));
modify(1,1,n,start[x],end[x],0);
}
}
return 0;
}