真没有想到,这竟然会是一道NOI的原题,听RQY说,这套题是北大出的,北大脑抽认为树剖很难。。。

只恨没有早学几年OI,只A这一道题也可以出去吹自己一A了NOI原题啊

好了,梦该醒了,我们来看题

以后放链接不放题面了,洛谷题面直接拷出来总是很迷

我是题面

读完题,我们会发现,这道题,好像是,树剖???裸题???

没错,就是裸题

题目要求两种操作,先说第一种

安装程序\(x\),并输出修改状态的程序数目。

就是从\(x\)一直查询+更新到根节点呗,节点数目减去已安装的数目就是答案,先查询,再更新,当然,操作和到一起也可以

第二种操作更无语,卸载程序\(x\)并输出修改状态的程序数目

说白了也就是看\(x\)的子树上安装了几个程序呗

那么线段树上,我们用1表示已安装,0表示未安装,一切就很显然了

上代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#define ll long long
#define gc() getchar()
#define maxn 100005
using namespace std;

inline ll read(){
    ll a=0;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}
void write(ll a){
    if(a>9)write(a/10);
    putchar(a%10+'0');
}
int n,m;

int tot,head[maxn];
struct ahaha1{
    int to,next;
}e[maxn<<1];
inline void add(int u,int v){
    e[tot].to=v;e[tot].next=head[u];head[u]=tot++;
}

int f[maxn],sz[maxn],son[maxn],dep[maxn];
void dfs(int u,int fa){
    int maxa=0;sz[u]=1;
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;if(v==fa)continue;
        f[v]=u;dep[v]=dep[u]+1;
        dfs(v,u);sz[u]+=sz[v];
        if(sz[v]>maxa)maxa=sz[v],son[u]=v;
    }
}
int top[maxn],in[maxn],b[maxn];
void dfs(int u,int fa,int topf){
    top[u]=topf;in[u]=++tot;b[tot]=u;
    if(!son[u])return;
    dfs(son[u],u,topf);
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;if(v==fa||v==son[u])continue;
        dfs(v,u,v);
    }
}

#define lc p<<1
#define rc p<<1|1
struct ahaha2{
    int v,lz;
    ahaha2(){
        lz=-1;
    }
}t[maxn<<2];
inline void pushup(int p){
    t[p].v=t[lc].v+t[rc].v;
}
inline void pushdown(int p,int l,int r){
    int m=l+r>>1;
    t[lc].v=t[p].lz*(m-l+1);t[lc].lz=t[p].lz;
    t[rc].v=t[p].lz*(r-m);t[rc].lz=t[p].lz;
    t[p].lz=-1;
}
ll query1(int p,int l,int r,int L,int R){    //安装程序的操作
    if(l>R||r<L)return 0;
    if(L<=l&&r<=R){int v=t[p].v;t[p].v=r-l+1;t[p].lz=1;return v;}    //通过先存储返回值然后进行修改来合并操作
    int m=l+r>>1;if(t[p].lz!=-1)pushdown(p,l,r);
    int l1=query1(lc,l,m,L,R),r1=query1(rc,m+1,r,L,R);    //同上
    pushup(p);
    return l1+r1;
}
ll query2(int p,int l,int r,int L,int R){     //卸载程序的操作
    if(l>R||r<L)return 0;
    if(L<=l&&r<=R){int v=t[p].v;t[p].v=0;t[p].lz=0;return v;}
    int m=l+r>>1;if(t[p].lz!=-1)pushdown(p,l,r);
    int l1=query2(lc,l,m,L,R),r1=query2(rc,m+1,r,L,R);
    pushup(p);
    return l1+r1;
}

inline void solve_1(){
    int x=read(),ans1=0,ans2=0;
    while(top[x]){
        ans1+=in[x]-in[top[x]]+1;    //别忘了+1
        ans2+=query1(1,1,n,in[top[x]],in[x]);
        x=f[top[x]];
    }
    ans1+=in[x]-in[0]+1;
    ans2+=query1(1,1,n,in[0],in[x]);
    write(ans1-ans2);putchar('\n');
}
inline void solve_2(){
    int x=read();
    write(query2(1,1,n,in[x],in[x]+sz[x]-1));putchar('\n');   //直接子树比安装简单多了
}

int main(){memset(head,-1,sizeof head);
    n=read();
    for(int i=1;i<n;++i){
        int a=read();
        add(a,i);
    }
    tot=0;dfs(0,-1);dfs(0,-1,0);
    m=read();
    string z;
    for(int i=1;i<=m;++i){
        cin>>z;
        switch(z[0]){
            case 'i':solve_1();break;
            case 'u':solve_2();break;
        }
    }
    return 0;
}