【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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值