P2146 [NOI2015]软件包管理器

传送门

非常显然的一道树剖

用线段树维护区间为1区间为0

因为修改只有在根到节点

甚至不用维护深度

因为一开始节点都为0

甚至不用线段树的 build 操作

比模板还简单...

至于询问修改了多少就只要把修改后的数量减去修改前的数量

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

const int N=1e5+7;
int n,m;

vector <int> v[N];
int fa[N],son[N],sz[N];//甚至不用维护深度
inline void dfs1(int x,int f)
{
    fa[x]=f; sz[x]=1;
    int mxsz=0,len=v[x].size();
    for(int i=0;i<len;i++)
    {
        int u=v[x][i];
        if(u==f) continue;
        dfs1(u,x); sz[x]+=sz[u];
        if(sz[u]>mxsz)
        {
            mxsz=sz[u];
            son[x]=u;
        }
    }
}
int Top[N],id[N],cnt;
inline void dfs2(int x,int topp)
{
    id[x]=++cnt; Top[x]=topp;
    if(!son[x]) return;
    dfs2(son[x],topp);
    int len=v[x].size();
    for(int i=0;i<len;i++)
    {
        int u=v[x][i];
        if(u==fa[x]||u==son[x]) continue;
        dfs2(u,u);
    }
}

//----------------------线段树--------------------------
//甚至没有build函数
int t[N<<2],add[N<<2],clr[N<<2];//add和clr为懒标记
inline void push(int &o,int &l,int &r)//下传懒标记
{
    int lc=o<<1,rc=o<<1|1,mid=l+r>>1;
    if(clr[o])//区间为0
    {
        add[lc]=add[rc]=0;
        t[lc]=t[rc]=0;
        clr[lc]=clr[rc]=1; clr[o]=0;
    }
    if(add[o])//区间为1
    {
        add[lc]=1; add[rc]=1;
        t[lc]=mid-l+1; t[rc]=r-mid;
        add[o]=0;
    }
}
void Add(int o,int l,int r,int ql,int qr)//区间修改为1
{
    if(l>=ql&&r<=qr)
    {
        add[o]=1; t[o]=r-l+1;
        return;
    }
    push(o,l,r);
    int mid=l+r>>1;
    if(ql<=mid) Add(o<<1,l,mid,ql,qr);
    if(qr>mid) Add(o<<1|1,mid+1,r,ql,qr);
    t[o]=t[o<<1]+t[o<<1|1];
}
void Clr(int o,int l,int r,int ql,int qr)//区间清0
{
    if(l>=ql&&r<=qr)
    {
        add[o]=t[o]=0; clr[o]=1;
        return;
    }
    push(o,l,r);
    int mid=l+r>>1;
    if(ql<=mid) Clr(o<<1,l,mid,ql,qr);
    if(qr>mid) Clr(o<<1|1,mid+1,r,ql,qr);
    t[o]=t[o<<1]+t[o<<1|1];
}
int query(int o,int l,int r,int ql,int qr)//询问区间和
{
    if(l>=ql&&r<=qr) return t[o];
    push(o,l,r);
    int mid=l+r>>1,res=0;
    if(ql<=mid) res+=query(o<<1,l,mid,ql,qr);
    if(qr>mid) res+=query(o<<1|1,mid+1,r,ql,qr);
    t[o]=t[o<<1]+t[o<<1|1];
    return res;
}
//---------------------线段树-------------------------

void Ins(int x)//安装一个软件
{
    int res=0;
    while(x)//给根到节点的一条链安装软件,根为0号节点
    {
        res-=query(1,1,n,id[Top[x]],id[x]);//先记录一下安装前的状态
        Add(1,1,n,id[Top[x]],id[x]);
        res+=query(1,1,n,id[Top[x]],id[x]);//再加上安装后的状态
        x=fa[Top[x]];
    }
    res-=query(1,1,n,id[Top[x]],id[x]);
    Add(1,1,n,1,1);//注意最后还要考虑给根节点安装
    res+=query(1,1,n,id[Top[x]],id[x]);
    printf("%d
",res);
}
void Uni(int x)//卸载一个软件
{
    printf("%d
", query(1,1,n,id[x],id[x]+sz[x]-1) );
    Clr(1,1,n,id[x],id[x]+sz[x]-1);//直接把整个子树清空就好了
}

int main()
{
    int a;
    n=read();
    for(int i=1;i<n;i++)
    {
        a=read();
        v[a].push_back(i);
    }
    dfs1(0,0); dfs2(0,0);
    
    m=read(); char ch[10];
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ch); a=read();
        if(ch[0]=='i') Ins(a);
        else Uni(a);
    }
    return 0;
}

 

原文地址:https://www.cnblogs.com/LLTYYC/p/9764597.html