洛谷 P2146 [NOI2015]软件包管理器

1 P2146 [NOI2015]软件包管理器

2 题目描述

时间限制 \(1s\) | 空间限制 \(125M\)

你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包 \(a\) 依赖软件包 \(b\),那么安装软件包 \(a\) 以前,必须先安装软件包 \(b\)。同时,如果想要卸载软件包 \(b\),则必须卸载软件包 \(a\)

现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除 \(0\) 号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而 \(0\) 号软件包不依赖任何一个软件包。且依赖关系不存在环(即不会存在 \(m\) 个软件包 \(a_1,a_2, \dots, a_m\),对于 $i <m $, \(a_i\) 依赖 \(a_i+1\) 的情况)。

现在你要为你的软件包管理器写一个依赖解决程序。根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。

注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为 \(0\)

数据范围:\(n\) 表示软件包个数,\(q\) 表示操作个数。\(n \le 100000, q \le 1000000\)

3 题解

我们发现,如果把两个软件包的依赖关系表示为一条有向边,那么最终呈现出来的一定是一棵树,其中顶点 \(0\) 的出度为 \(0\),正如题目下方给出的树一样。那么此时,如果我们要安装 \(x\),那么所有 \(x\)\(0\) 的路径上的软件包都被必须安装。如果我们要卸载 \(x\),那么所有能走到它的,也就是 \(x\) 的子树中的所有软件包都必须被卸载。这里为了便于深搜,我们反向建边,即从第 \(i\) 号软件包所依赖的软件包连向 \(i\)。这里我们用 \(0\) 表示没有被安装,\(1\) 表示已经被安装,于是每个节点就有了 \(1\)\(0\) 两种权值。

这样一来,题目就被简化为:给出一棵树和两种操作,第一种操作是输出 \(x\) 号节点到根的路径上所有节点的个数与权值之和的差,并将所有节点的权值都赋为 \(1\);第二种操作是输出 \(x\) 号节点的子树中所有权值之和,并将所有子树中的节点的权值都赋为 \(0\)

这里我们如果直接 \(dfs\) 硬性修改,时间复杂度为 \(O(nq)\),无法通过此题。这里我们考虑用树链剖分来解决这个问题。

树链剖分大致是一种通过特殊处理,将树上特殊链的节点序号变为连续,使得我们可以用数据结构进行维护的一种神奇算法(具体内容还请大家自行学习,作者表达能力不好,很难正确地将算法模板讲述清晰)。

这里加上树链剖分后的时间复杂度为 \(O(q \space log_2^2 n)\),可以接受。

4 代码(空格警告):

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e5+10;
int n, q, u, tot, cnt;
int head[N], ver[N], last[N];
int id[N], top[N], dep[N], siz[N], f[N], son[N];
string opt;
struct node
{
    int l, r, sum, tag;
}t[N*4];
void build(int p, int l, int r)
{
    t[p].l = l;
    t[p].r = r;
    t[p].sum = 0;
    t[p].tag = -1;
    if (l == r) return ;
    int mid = (l+r)/2;
    build(p*2, l, mid);
    build(p*2+1, mid+1, r);
}
void pushdown(int p)
{
    if (t[p].tag != -1)
    {
        t[p*2].sum = (t[p*2].r - t[p*2].l + 1) * t[p].tag;
        t[p*2+1].sum = (t[p*2+1].r - t[p*2+1].l + 1) * t[p].tag;
        t[p*2].tag = t[p].tag;
        t[p*2+1].tag = t[p].tag;
        t[p].tag = -1;
    }
}
void modify(int p, int l, int r, int d)
{
    if (t[p].l >= l && t[p].r <= r)
    {
        t[p].sum = (t[p].r - t[p].l + 1) * d;
        t[p].tag = d;
        return ;
    }
    pushdown(p);
    int mid = (t[p].l + t[p].r) / 2;
    if (l <= mid) modify(p*2, l, r, d);
    if (r > mid) modify(p*2+1, l, r, d);
    t[p].sum = t[p*2].sum + t[p*2+1].sum;
}
int query(int p, int l, int r)
{
    if (t[p].l >= l && t[p].r <= r) return t[p].sum;
    pushdown(p);
    int mid = (t[p].l + t[p].r) / 2, ans = 0;
    if (l <= mid) ans += query(p*2, l, r);
    if (r > mid) ans += query(p*2+1, l, r);
    return ans;
}
void add(int x, int y)
{
    ver[++tot] = y;
    last[tot] = head[x];
    head[x] = tot;
}
void dfs1(int x, int fa, int depth)
{
    f[x] = fa;
    dep[x] = depth;
    siz[x] = 1;
    for (int i = head[x]; i; i = last[i])
    {
        int y = ver[i];
        dfs1(y, x, depth+1);
        siz[x] += siz[y];
        if (siz[y] > siz[son[x]]) son[x] = y;
    }
}
void dfs2(int x, int t)
{
    id[x] = ++cnt;
    top[x] = t;
    if (!son[x]) return ;
    dfs2(son[x], t);
    for (int i = head[x]; i; i = last[i])
    {
        int y = ver[i];
        if (y == son[x]) continue;
        dfs2(y, y);
    }
}
int sum(int x, int y)
{
    int fx = top[x], fy = top[y], ans = 0;
    while (fx != fy)
    {
        if (dep[fx] >= dep[fy])
        {
            ans += query(1, id[fx], id[x]);
            x = f[fx];
            fx = top[x];
        }
        else
        {
            ans += query(1, id[fy], id[y]);
            y = f[fy];
            fy = top[y];
        }
    }
    if (dep[x] <= dep[y]) ans += query(1, id[x], id[y]);
    else ans += query(1, id[y], id[x]);
    return ans;
}
void change(int x, int y, int d)
{
    int fx = top[x], fy = top[y];
    while (fx != fy)
    {
        if (dep[fx] >= dep[fy])
        {
            modify(1, id[fx], id[x], d);
            x = f[fx];
            fx = top[x];
        }
        else
        {
            modify(1, id[fy], id[y], d);
            y = f[fy];
            fy = top[y];
        }
    }
    if (dep[x] <= dep[y]) modify(1, id[x], id[y], d);
    else modify(1, id[y], id[x], d);
}
int main()
{
    scanf("%d", &n);
    build(1, 1, n);
    for (int i = 2; i <= n; i++)
    {
        scanf("%d", &u);
        u++;
        add(u, i);
    }
    dfs1(1, 0, 1);
    dfs2(1, 1);
    scanf("%d", &q);
    int x;
    for (int i = 1; i <= q; i++)
    {
        cin >> opt;
        scanf("%d", &x);
        x++;
        if (opt == "install")
        {
            printf("%d\n", dep[x] - sum(x, 1));
            change(x, 1, 1);
        }
        else
        {
            printf("%d\n", query(1, id[x], id[x] + siz[x] - 1));
            modify(1, id[x], id[x] + siz[x] - 1, 0);
        }
    }
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-05-04 14:24  David24  阅读(61)  评论(0编辑  收藏  举报