【树链剖分+线段树】[noi2015]软件包管理器

题目
先树链剖分,再根据dfn建立线段树(在同一根树链中的点dfn是连续的),然后,就很好做了。

#include<cstdio>
#define MAXN 100000
int n,fa[MAXN+10],size[MAXN+10],pos[MAXN+10],length[MAXN+10],bl[MAXN+10],Q,dfn[MAXN+10],dcnt,ed[MAXN+10];
bool f[MAXN+10];
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
struct node{
    int v;
    node *next;
}*adj[MAXN+10],edge[MAXN+10],*ecnt=edge;
struct seg_tree{
    int sum,tag,l,r;
}tree[MAXN*4+10];
void clear(int i){
    tree[i<<1].sum=(tree[i<<1].r-tree[i<<1].l+1)*tree[i].tag;
    tree[i<<1].tag=tree[i].tag;
    tree[(i<<1)|1].sum=(tree[(i<<1)|1].r-tree[(i<<1)|1].l+1)*tree[i].tag;
    tree[(i<<1)|1].tag=tree[i].tag;
    tree[i].tag=-1;
}
void build(int i,int l,int r){
    tree[i].l=l,tree[i].r=r;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
}
void insert(int i,int l,int r,int val){
    if(tree[i].l>r||tree[i].r<l)
        return;
    if(tree[i].l>=l&&tree[i].r<=r){
        tree[i].sum=((tree[i].r-tree[i].l)+1)*val;
        tree[i].tag=val;
        return;
    }
    if(tree[i].tag>=0)
        clear(i);
    insert(i<<1,l,r,val);
    insert((i<<1)|1,l,r,val);
    tree[i].sum=tree[i<<1].sum+tree[(i<<1)|1].sum;
}
int find(int i,int l,int r){
    if(tree[i].l>=l&&tree[i].r<=r)
        return tree[i].sum;
    if(tree[i].r<l||tree[i].l>r)
        return 0;
    if(tree[i].tag>=0)
        clear(i);
    return find(i<<1,l,r)+find((i<<1)|1,l,r);
}
void addedge(int u,int v){
    node *p=++ecnt;
    p->v=v;
    p->next=adj[u];
    adj[u]=p;
}
void dfs1(int u){
    size[u]=1;
    for(node *p=adj[u];p;p=p->next){
        dfs1(p->v);
        size[u]+=size[p->v];
    }
}
void dfs2(int u,int len){
    dfn[u]=++dcnt;
    int heavy=n;
    for(node *p=adj[u];p;p=p->next)
        if(size[p->v]>size[heavy])
            heavy=p->v;
    if(heavy==n){
        ed[u]=dcnt;
        int tp=u,i;
        for(i=1;i<len;i++)
            tp=fa[tp];
        length[tp]=len;
        for(i=len;i;i--){
            pos[u]=i;
            bl[u]=tp;
            u=fa[u];
        }
        return;
    }
    dfs2(heavy,len+1);
    for(node *p=adj[u];p;p=p->next)
        if(p->v!=heavy)
            dfs2(p->v,1);
    ed[u]=dcnt;
}
void init(){
    Read(n);
    for(int i=1;i<n;i++){
        Read(fa[i]);
        addedge(fa[i],i);
    }
    build(1,1,n);
    dfs1(0);
    dfs2(0,1);
}
int query(int a){
    int ret=0,num=0;
    while(bl[a]){
        num+=pos[a];
        ret+=find(1,dfn[bl[a]],dfn[a]);
        insert(1,dfn[bl[a]],dfn[a],1);
        a=fa[bl[a]];
    }
    num+=pos[a];
    ret+=find(1,dfn[bl[a]],dfn[a]);
    insert(1,dfn[bl[a]],dfn[a],1);
    return num-ret;
}
void solve(){
    char s[15];
    int a;
    Read(Q);
    while(Q--){
        scanf("%s",s);
        Read(a);
        if(s[0]=='i')
            printf("%d\n",query(a));
        else{
            printf("%d\n",find(1,dfn[a],ed[a]));
            insert(1,dfn[a],ed[a],0);
        }
    }
}
int main()
{
    init();
    solve();
}

你可能感兴趣的:(C++,线段树,树链剖分,NOI)