• 【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
    ",dep[x]+1-treequery(0,x));
    	  treemodify(0,x,1);
    	}
    	if (op[0]=='u')
    	{
    	  printf("%d
    ",query(1,1,n,start[x],end[x]));
    	  modify(1,1,n,start[x],end[x],0);
    	}
      }
      
      return 0;
    }
    


  • 相关阅读:
    卷积神经网络中的傅里叶变换:1024x1024 的傅里叶卷积
    在不平衡数据上使用AUPRC替代ROCAUC
    论文推荐:TResNet改进ResNet 实现高性能 GPU 专用架构并且效果优于 EfficientNet
    信息安全风险评估
    剑指 Offer
    Docker创建容器时默认的共享内存shm太小报错,程序无法正常运行
    VS Code 连接访问本地主机上的Docker容器
    关于protobuf报错:If this call came from a _pb2.py file, your generated code is out of date and must be regenerated with protoc >= 3.19.0.
    训练神经网络时报错:can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
    我学习使用五笔的经验
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793736.html
Copyright © 2020-2023  润新知