NOI2015 软件包管理器 树剖+线段树

题目背景是一个安装与卸载软件的系统,软件之间关系是一棵树,以0为根节点。当你安装一个软件时需要先安装它的所有祖先,卸载时则要先卸载整个子树。对每一次操作询问改变状态的软件数目。

思路:dfs序做树剖,用线段树维护数据。

#include<iostream>
#include<vector>
#include<stdio.h>
#define ls (id*2)
#define rs (id*2+1)
using namespace std;
vector<int> lin[100005];
int in[100005],size[100005],fa[100005],top[100005],n,num,ans;
struct node
{
    int val;
    int lazy;
}t[100005*4];
int abs(int a)
{
    return a>=0?a:-a;
}
void push_down(int id,int l,int mid,int r)
{
    if (t[id].lazy==-1)return;
    t[ls].lazy=t[id].lazy;
    t[rs].lazy=t[id].lazy;
    t[ls].val=(mid-l+1)*t[id].lazy;
    t[rs].val=(r-mid)*t[id].lazy;
    t[id].lazy=-1;
}
void push_up(int id)
{
    t[id].val=t[ls].val+t[rs].val;
}
void build(int id,int l,int r)
{
    if(l==r)
    {
        t[id].val=0;
        t[id].lazy=-1;//改成-1!切记! 
        return;
    }
    int mid=(l+r)/2;
    build(ls,l,mid);
    build(rs,mid+1,r);
    push_up(id);
}
void insert(int id,int l,int r,int L,int R,int V)
{
    if(r<L||l>R) return;
    if(r<=R && l>=L)
    {
        ans+=abs(V*(r-l+1)-t[id].val);
        t[id].val=V*(r-l+1);
        t[id].lazy=V;
        return;
    }
    int mid=(l+r)/2;
    push_down(id,l,mid,r);
    insert(ls,l,mid,L,R,V);
    insert(rs,mid+1,r,L,R,V);
    push_up(id);
}
void dfs1(int x,int pre)
{
    in[x]=++num;
    fa[x]=pre;
    for (int i=0;i<lin[x].size();i++)
    {
        int nex=lin[x][i];
        if (nex==pre)   continue;
        dfs1(nex,x);
    }
    size[x]=num-in[x]+1;//必须+1,否则dfs2中判断叶子节点很麻烦。
}
void dfs2(int x,int t)
{
    in[x]=++num;
    top[x]=t;
    if (size[x]==1) return;//叶子节点直接返回,否则会死循环(不能用size[x]=0做!)
    int msize=0,heavy=0;//都要赋初始值!
    for (int i=0;i<lin[x].size();i++)
    {
        int nex=lin[x][i];
        if (nex==fa[x]) continue;
        if (size[nex]>msize)
        {
            msize=size[nex];
            heavy=nex;
        }
    }
    dfs2(heavy,t);//先走重儿子,其他随意
    for (int i=0;i<lin[x].size();i++)
    {
        int nex=lin[x][i];
        if (nex==fa[x] || nex==heavy) continue;
        dfs2(nex,nex);
    }
}
int main()
{
    cin>>n;
    for (int i=1;i<n;i++)
    {
        int a;
        cin>>a;//编号都+1,防止爆线段树 
        lin[a+1].push_back(i+1);
        lin[i+1].push_back(a+1);
    }
    num=0;  dfs1(1,0);
    num=0;  dfs2(1,1);
    int q;  
    cin>>q;
    char s[15];
    while (q--)
    {
        int a;
        scanf("%s",s);//一定不要用cin!!!!T了半个小时...
        scanf("%d",&a);
        a++;
        if (s[0]=='i')
        {
            ans=0;
            while (a!=0)
            {
                insert(1,1,n,in[top[a]],in[a],1);
                a=fa[top[a]];
            } 
            printf("%d\n",ans);
        }
        if (s[0]=='u')
        {
            ans=0;
            insert(1,1,n,in[a],in[a]+size[a]-1,0);
            printf("%d\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值