【BZOJ4196】【codevs4621】软件包管理器,树链剖分练习

传送门1
传送门2
写在前面:感冒,在家休养,已陷入动物城不能自拔
思路:真的是莫名其妙就A了……加了个lazy标记什么的而且用了一些奇怪姿势的线段树?删一个包就是对它的子树进行统计,计算有多少个已安装的子节点,安一个包就是看它到根的路径上有多少没安装过的包(其实可以一直找父亲直到有安装过的?),po主反正是闲的蛋疼乱搞了一坨,在BZOJ上跑了9s+……
注意:如果你像我一样蛋疼的话lazy标记加的时候最好小心些
代码:

#include<bits/stdc++.h>
using namespace std;
int tot,n,x,q,cnt;
int first[100010],fa[100010],son[100010],pre[100010],dep[100010],siz[100010],top[100010],L[100010],R[100010];
char ch;
struct os
{
    int u,v,next;
}e[200010];
struct node
{
    int lazy,sum;
}tree[800010];
void add(int x,int y)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].next=first[x];
    first[x]=tot;
}
void pushdown(int now,int l,int r)
{
    if (!tree[now].lazy) return;
    int mid=(l+r)>>1;
    tree[now<<1].sum=(tree[now].lazy-1)*(mid-l+1);
    tree[now<<1].lazy=tree[now].lazy;
    tree[now<<1|1].sum=(tree[now].lazy-1)*(r-mid);
    tree[now<<1|1].lazy=tree[now].lazy;
    tree[now].lazy=0;
}
void dfs1(int now)
{
    siz[now]=1;
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=fa[now])
    {
        fa[e[i].v]=now;
        dep[e[i].v]=dep[now]+1;
        dfs1(e[i].v);
        if (siz[e[i].v]>siz[son[now]]) son[now]=e[i].v;
        siz[now]+=siz[e[i].v];
    }
}
void dfs2(int now,int tp)
{
    L[now]=++cnt;
    pre[cnt]=now;
    top[now]=tp;
    if (son[now]) dfs2(son[now],tp);
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=son[now]&&e[i].v!=fa[now])
    dfs2(e[i].v,e[i].v);
    R[now]=cnt;
}
void update(int now,int begin,int end,int l,int r,int flag)//flag是打标记用的,可以随便搞搞
{
    if (l<=begin&&end<=r)
    {
        tree[now].lazy=flag+1;
        tree[now].sum=(end-begin+1)*flag;
        return;
    }
    pushdown(now,begin,end);
    int mid=(begin+end)>>1;
    if (l<=mid) update(now<<1,begin,mid,l,r,flag);
    if (r>mid) update(now<<1|1,mid+1,end,l,r,flag);
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
}
int get(int now,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return tree[now].sum;
    pushdown(now,begin,end);
    int ans=0,mid=(begin+end)>>1;
    if (mid>=l) ans+=get(now<<1,begin,mid,l,r);
    if (mid<r) ans+=get(now<<1|1,mid+1,end,l,r);
    return ans;
}
void solve(int l,int r)
{
    int ans,f1=top[l],f2=top[r];
    ans=dep[l]-dep[r]+1;//某个点到根路径上所经过的点数就是深度之差+1
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
        ans-=get(1,1,cnt,L[f1],L[l]);
        update(1,1,cnt,L[f1],L[l],1);
        l=fa[f1];f1=top[l];
    }
    if (dep[l]>dep[r]) swap(l,r);
    ans-=get(1,1,cnt,L[l],L[r]);
    update(1,1,cnt,L[l],L[r],1);
    printf("%d\n",ans);
}
main()
{
    scanf("%d",&n);
    for (int i=2;i<=n;i++)
    scanf("%d",&x),
    add(x+1,i),
    add(i,x+1);
    dfs1(1);
    dfs2(1,1);
    scanf("%d",&q);
    while (q--)
    {
        ch=getchar();
        while (ch!='i'&&ch!='u') ch=getchar();
        while (getchar()!=' ');
        scanf("%d",&x);
        x++;
        if (ch=='i') solve(x,1);
        else
        {
            int ans=get(1,1,cnt,L[x],R[x]);
            printf("%d\n",ans);
            update(1,1,cnt,L[x],R[x],0);
        }
    }
}

你可能感兴趣的:(【BZOJ4196】【codevs4621】软件包管理器,树链剖分练习)