zoukankan      html  css  js  c++  java
  • 洛谷P2146 [NOI2015]软件包管理器 题解 树链剖分+线段树

    题目链接:https://www.luogu.org/problem/P2146
    本题涉及算法:

    • 树链剖分;
    • 线段树(区间更新及求和,涉及懒惰标记)

    然后对于每次 install x ,需要将 x1 的路径上面的点全都置为1。
    那么在置为1之前统计一下节点数量 num1,
    在置为1之后统计一下节点数量 num2,
    答案就是 num2 - num1(当然,也可以通过节点深度 dep[x] 来获得节点数量)。

    对于每次 unistall x,需要将 x 为根的子树上面的点全都置为0。
    那么在置为0之前统计一下权值为1的节点数量 num1,
    在置为0之后统计一下权值为1的节点数量 num2,
    答案就是 num1-num2(当然,num2 其实就等于 0)。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    #define INF (1<<29)
    const int maxn = 100010;
    int fa[maxn],
    	dep[maxn],
    	size[maxn],
    	son[maxn],
    	top[maxn],
    	seg[maxn], seg_cnt,
    	rev[maxn],
    	n,
    	sumv[maxn<<2], lazy[maxn<<2];
    vector<int> g[maxn];
    void dfs1(int u, int p) {
    	size[u] = 1;
    	for (vector<int>::iterator it = g[u].begin(); it != g[u].end(); it ++) {
    		int v = (*it);
    		if (v == p) continue;
    		fa[v] = u;
    		dep[v] = dep[u] + 1;
    		dfs1(v, u);
    		size[u] += size[v];
    		if (size[v] >size[son[u]]) son[u] = v;
    	}
    }
    void dfs2(int u, int tp) {
    	seg[u] = ++seg_cnt;
    	rev[seg_cnt] = u;
    	top[u] = tp;
    	if (son[u]) dfs2(son[u], tp);
    	for (vector<int>::iterator it = g[u].begin(); it != g[u].end(); it ++) {
    		int v = (*it);
    		if (v == fa[u] || v == son[u]) continue;
    		dfs2(v, v);
    	}
    }
    #define lson l, mid, rt<<1
    #define rson mid+1, r, rt<<1|1
    void push_down(int rt, int len) {
    	if (lazy[rt] != -1) {
    		int l_len=len-len/2, r_len = len/2;
    		lazy[rt<<1] = lazy[rt];
    		lazy[rt<<1|1] = lazy[rt];
    		sumv[rt<<1] = lazy[rt] * l_len;
    		sumv[rt<<1|1] = lazy[rt] * r_len;
    		lazy[rt] = -1;
    	}
    }
    void push_up(int rt) {
    	sumv[rt] = sumv[rt<<1] + sumv[rt<<1|1];
    }
    void build(int l, int r, int rt) {
    	lazy[rt] = -1;
    	int mid = (l + r) / 2;
    	if (l == r) {
    		sumv[rt] = 0;
    		return;
    	}
    	build(lson); build(rson);
    	push_up(rt);
    }
    void update(int L, int R, long long v, int l, int r, int rt) {
    	if (L <= l && r <= R) {
    		sumv[rt] = (r-l+1) * v;
    		lazy[rt] = v;
    		return;
    	}
    	push_down(rt, r-l+1);
    	int mid = (l + r) / 2;
    	if (L <= mid) update(L, R, v, lson);
    	if (R > mid) update(L, R, v, rson);
    	push_up(rt);
    }
    long long query_sum(int L, int R, int l, int r, int rt) {
    	if (L <= l && r <= R) return sumv[rt];
    	push_down(rt, r-l+1);
    	int mid = (l + r) / 2;
    	long long tmp = 0;
    	if (L <= mid) tmp += query_sum(L, R, lson);
    	if (R > mid) tmp += query_sum(L, R, rson);
    	return tmp;
    }
    int t_ask(int u) {
    	int res = 0;
    	while (top[u] != 1) {
    		res += query_sum(seg[top[u]], seg[u], 1, n, 1);
    		u = fa[top[u]];
    	}
    	res += query_sum(seg[1], seg[u], 1, n, 1);
    	return res;
    }
    void t_update(int u) {
    	while (top[u] != 1) {
    		update(seg[top[u]], seg[u], 1, 1, n, 1);
    		u = fa[top[u]];
    	}
    	update(seg[1], seg[u], 1, 1, n, 1);
    }
    int m, x;
    string op;
    int main() {
    	cin >> n;
    	for (int i = 2; i <= n; i ++) {
    		cin >> x;
    		g[x+1].push_back(i);
    	}
    	dep[1] = fa[1] = 1;
    	dfs1(1, -1);
    	dfs2(1, 1);
    	build(1, n, 1);
    	cin >> m;
    	while (m --) {
    		cin >> op >> x;
    		x ++;
    		if (op == "install") {
    			int num1 = t_ask(x);
    			t_update(x);
    			int num2 = t_ask(x);
    			cout << num2 - num1 << endl;
    		}
    		else {	// uninstall
    			int num1 = query_sum(seg[x], seg[x]+size[x]-1, 1, n, 1);
    			update(seg[x], seg[x]+size[x]-1, 0, 1, n, 1);
    			int num2 = query_sum(seg[x], seg[x]+size[x]-1, 1, n, 1);
    			cout << num1 - num2 << endl;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    SQLSERVER 2008 编辑所有或者任意行
    在SQL2008和2012里面怎么让显示全部行和编辑 全部而不是200和1000
    sql server 提取汉字/数字/字母的方法
    [再寄小读者之数学篇](2014-05-12 曲线的弧长计算)
    [复变函数]第23堂课 6.2 用留数定理计算实积分 (续)
    [再寄小读者之数学篇](2014-04-23 行列式的导数)
    [再寄小读者之数学篇](2014-04-22 平方差公式在矩阵中的表达)
    康乐不风流之爱解题的pde灌水王张祖锦
    [复变函数]第22堂课 6.2 用留数定理计算实积分
    [Tex学习笔记]数学公式再次测试
  • 原文地址:https://www.cnblogs.com/quanjun/p/12005248.html
Copyright © 2011-2022 走看看