当前位置 博文首页 > correct:NOI 2015 软件包管理器(树链剖分)

    correct:NOI 2015 软件包管理器(树链剖分)

    作者:[db:作者] 时间:2021-09-21 21:02

    题目链接

    中文题,不写题目描述了,大致意思就是,在安装和卸载程序的时候,会有依赖项,假设 A A A依赖于 B B B,那么安装 A A A之前必须先安装 B B B,同理,卸载 B B B之前必须卸载 A A A,问每次安装或卸载程序,有多少个安装包的状态被改变了。

    当然,可以暴力求解,但是数据量不允许。

    首先明确这是一个树的模型,(题中说了每个程序仅依赖于一个),但是可能会有一个依赖链,即 C C C依赖 B B B B B B依赖于 A A A,而最终都会依赖到 0 0 0号节点(题中说的),因此安装的过程相当于求从这个节点到 0 0 0号节点的路径上有多少个未被安装的

    卸载的过程,则是求解以这个点未根的子树上,有多少个点被安装了

    OK,可以通过树剖,讲树上的路径转换为连续的区间,然后用线段树去维护每一个区间的信息即可。由于本文不是讲树剖的,因此不多赘述。这里仅提供一个树链剖分的讲解blog 树链剖分

    树剖后,可以很方便的处理这两个问题,对于安装过程而言,就是求两个点之间的路径上未安装的个数的和,用线段树维护每一个区间内的未被安装的个数即可,然后讲两个点不断上升至到达同一重链上,过程中还要计算每一条重链上的和,累加即可。

    对于卸载过程而言,因为一个点的子树上的所有的点的时间戳都是连续的,因此也直接转换为对线段树上的一段连续区间进行操作

    注意: 更新操作比较特殊,不再是累加,而是直接覆盖,例如对区间 [ 1 , 5 ] [1,5] [1,5]先进行操作,全部安装,下次操作它的子区间,是直接将子区间用新的标记覆盖了,因此线段树中标记下传需要一点小改动。

    因为我用线段树维护的是未安装的数量,因此卸载过程需要减法,当然也可以换成维护已安装的数量,甚至两个都维护。

    直接看代码吧:

    #include <bits/stdc++.h>
    #define mem(a, b) memset(a, b, sizeof a)
    using namespace std;
    const int N = 100100;
    int head[N], nex[N * 2], to[N * 2], cnt;
    void add(int a, int b) {
    	++cnt;
    	to[cnt] = b;
    	nex[cnt] = head[a];
    	head[a] = cnt;
    }
    int fa[N], depth[N], son[N], siz[N];
    void dfs1(int x, int dep) {
    	depth[x] = dep;
    	siz[x] = 1;
    	int max_size = -1;
    	for (int i = head[x]; ~i; i = nex[i]) {
    		int y = to[i];
    		if (y == fa[x])continue;
    		if (!depth[y]) {
    			fa[y] = x;
    			dfs1(y, dep + 1);
    			siz[x] += siz[y];
    			if (max_size == -1 | max_size < siz[y]) {
    				max_size = siz[y];
    				son[x] = y;
    			}
    		}
    	}
    }
    int dfn[N], rk[N], num, top[N];
    void dfs2(int x, int tp) {
    	dfn[x] = ++num;
    	rk[num] = x;
    	top[x] = tp;
    	if (son[x] == -1)return;
    	dfs2(son[x], tp);
    	for (int i = head[x]; ~i; i = nex[i]) {
    		int y = to[i];
    		if (y != son[x] && y != fa[x]) {
    			dfs2(y, y);
    		}
    	}
    }
    void init() {
    	mem(head, -1), mem(nex, -1), cnt = 0;
    	num = 0, mem(dfn, -1), mem(rk, -1), mem(top, -1);
    	mem(fa, -1), mem(depth, 0), mem(siz, 0), mem(son, -1);
    }
    struct p {
    	int l, r, sum, length, lazy;
    };
    typedef struct SegementTree {
    	p c[N * 4];
    	void build(int l, int r, int k) {
    		c[k].l = l;
    		c[k].r = r;
    		c[k].sum = 0;
    		c[k].length = 0;
    		c[k].lazy = 0;
    		if (l == r) {
    			c[k].length = 1;
    			c[k].sum = 1;
    			return;
    		}
    		int mid = (l + r) >> 1;
    		build(l, mid, k << 1);
    		build(mid + 1, r, k << 1 | 1);
    		c[k].length = c[k << 1].length + c[k << 1 | 1].length;
    		c[k].sum = c[k << 1].sum + c[k << 1 | 1].sum;
    	}
    	void down(int k) {// 标记下传
    		if (c[k].lazy) {
    			int lz = c[k].lazy;
    			c[k << 1].sum = lz == -1 ? c[k << 1].length : 0;
    			c[k << 1 | 1].sum = lz == -1 ? c[k << 1 | 1].length : 0;
    			c[k << 1].lazy = c[k << 1 | 1].lazy = c[k].lazy;
    			c[k].lazy = 0;
    		}
    	}
    	int query(int l, int r, int k) {
    		if (l <= c[k].l && r >= c[k]