[NOI2015]软件管理器题解

想不到自己才过了几天又想写一篇,今天带来的依旧是水题
emm其实就是道树链剖分的模拟题。
传送门:https://www.luogu.org/problemnew/show/P2146
因为只有0没依靠软件,且不会出现环,那么显然依赖关系可以把软件们连成一棵以0为根节点的树。
所以:

  1. 下载步骤就是就是将该节点到根节点的节点都变为下载状态。
  2. 卸载步骤就是将该节点和其子树的所有点变为卸载状态。

因此我只用在树上维护一个线段树记录有多少个节点状态为下载,并用总数与上次操作的总数做差就是答案,很显然可以用树链剖分解决。
代码如下:

#include"cstdio"
#include"algorithm"
#define MAXN 100000+10

using namespace std;

struct edge
{
  int v,next;
};

inline int read()
{
  char ch=getchar();int x=0,f=1;
  while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x*f;
}

edge eg[(MAXN)<<1];
int sumv[(MAXN)<<2],lazy_tag[(MAXN)<<2],situ[(MAXN)<<2];
int depth[MAXN],top[MAXN],siz[MAXN],sons[MAXN],fa[MAXN],id[MAXN];
int n,m,last=0,tops=0;
int p[MAXN];
int cnt=0;
char inse[11];

void addedge(int u,int v)
{
  eg[++tops].v=v;
  eg[tops].next=p[u];
  p[u]=tops;
}

void maintain(int o,int l,int r)
{
  situ[o]=situ[o<<1]&situ[o<<1|1];
  if(situ[o]) sumv[o]=r-l+1;
  else sumv[o]=sumv[o<<1]+sumv[o<<1|1];
}

void pushdown(int o,int l,int r)
{
  int mid=(l+r)>>1;
  if(lazy_tag[o]==1)
  {
    lazy_tag[o<<1]=1,sumv[o<<1]=mid-l+1,situ[o<<1]=1;
    lazy_tag[o<<1|1]=1,sumv[o<<1|1]=r-mid,situ[o<<1|1]=1;
  }
  else
    if(lazy_tag[o]==2)
    {
      lazy_tag[o<<1]=2,sumv[o<<1]=0,situ[o<<1]=0;
      lazy_tag[o<<1|1]=2,sumv[o<<1|1]=0,situ[o<<1|1]=0;
    }
  lazy_tag[o]=0;
}

void change(int o,int x,int y,int l,int r,int v)
{
  if(x<=l&&y>=r)
  {
    lazy_tag[o]=v;
    if(v==1)
      sumv[o]=r-l+1,situ[o]=1;
    else
      sumv[o]=0,situ[o]=0;
    return ;
  }
  int mid=(l+r)>>1;
  pushdown(o,l,r);
  if(mid>=x)
    change(o<<1,x,y,l,mid,v);
  if(mid<y)
    change(o<<1|1,x,y,mid+1,r,v);
  maintain(o,l,r);
}

void uninsta(int x)
{
  change(1,id[x],id[x]+siz[x]-1,0,n-1,2);
}

void insta(int x,int y)
{
  while(top[x]!=top[y])
  {
    if(depth[top[x]]>depth[top[y]]) swap(x,y);
    change(1,id[top[y]],id[y],0,n-1,1);
    y=fa[top[y]];
  }
  if(depth[x]>depth[y]) swap(x,y);
  change(1,id[x],id[y],0,n-1,1);
}

void dfs1(int x,int dep,int fath)
{
  siz[x]=1;
  depth[x]=dep;
  fa[x]=fath;
  int maxv=0;
  for(int i=p[x]; i; i=eg[i].next)
    if(eg[i].v!=fath)
    {
      dfs1(eg[i].v,dep+1,x);
      siz[x]+=siz[eg[i].v];
      maxv=maxv==0||siz[eg[i].v]>siz[maxv]?eg[i].v:maxv;
    }
  sons[x]=maxv;
}

void dfs2(int x,int tops)
{
  top[x]=tops;
  id[x]=cnt++;
  if(!sons[x]) return ;
  dfs2(sons[x],tops);
  for(int i=p[x]; i; i=eg[i].next)
  {
    int v=eg[i].v;
    if(v==fa[x]||v==sons[x]) continue;
    dfs2(v,v);
  }
}

int main()
{
  n=read();
  for(int i=1; i<n; i++)
  {
    int x=read();
    addedge(i,x);
    addedge(x,i);
  }
  dfs1(0,1,0);
  dfs2(0,0);
  m=read();
  for(int i=1; i<=m; i++)
  {
    scanf("%s",inse+1);
    int x=read(),ans;
    if(inse[1]=='i')
    {
      insta(0,x);
      ans=sumv[1]-last;
      last=sumv[1];
    }
    else
    {
      uninsta(x);
      ans=last-sumv[1];
      last=sumv[1];
    }
    printf("%d\n",ans);
  }
  return 0;
}

该代码situ数组是可以不建的,但我还是建了导致没有一次过。我van了。
又犯了许多沙茶错误emm/dk/dk/kel/kel。
还有速度似乎有点慢……
上次写得还蛮快的emm,算了就这样结束吧……emm

你可能感兴趣的:(入门题,多刷有益健康/斜眼笑,线段树,树链剖分)