NOI 2015 软件包管理器(树链剖分)
中文题,不写题目描述了,大致意思就是,在安装和卸载程序的时候,会有依赖项,假设依赖于,那么安装之前必须先安装,同理,卸载之前必须卸载,问每次安装或卸载程序,有多少个安装包的状态被改变了。
当然,可以暴力求解,但是数据量不允许。
首先明确这是一个树的模型,(题中说了每个程序仅依赖于一个),但是可能会有一个依赖链,即依赖,依赖于,而最终都会依赖到号节点(题中说的),因此安装的过程相当于求从这个节点到号节点的路径上有多少个未被安装的。
而卸载的过程,则是求解以这个点未根的子树上,有多少个点被安装了。
OK,可以通过树剖,讲树上的路径转换为连续的区间,然后用线段树去维护每一个区间的信息即可。由于本文不是讲树剖的,因此不多赘述。这里仅提供一个树链剖分的讲解blog 树链剖分
树剖后,可以很方便的处理这两个问题,对于安装过程而言,就是求两个点之间的路径上未安装的个数的和,用线段树维护每一个区间内的未被安装的个数即可,然后讲两个点不断上升至到达同一重链上,过程中还要计算每一条重链上的和,累加即可。
对于卸载过程而言,因为一个点的子树上的所有的点的时间戳都是连续的,因此也直接转换为对线段树上的一段连续区间进行操作。
注意: 更新操作比较特殊,不再是累加,而是直接覆盖,例如对区间先进行操作,全部安装,下次操作它的子区间,是直接将子区间用新的标记覆盖了,因此线段树中标记下传需要一点小改动。
因为我用线段树维护的是未安装的数量,因此卸载过程需要减法,当然也可以换成维护已安装的数量,甚至两个都维护。
直接看代码吧:
#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].r)return c[k].sum;
down(k);
int mid = (c[k].l + c[k].r) >> 1;
int ans = 0;
if (l <= mid)ans += query(l, r, k << 1);
if (r > mid)ans += query(l, r, k << 1 | 1);
return ans;
}
void update(int l, int r, int k, int d) {
if (l <= c[k].l && r >= c[k].r) {//更新操作中特殊的地方
if (d == 1) {
c[k].sum = 0;
c[k].lazy = d;
}
else if (d == -1) {
c[k].sum = c[k].length;
c[k].lazy = d;
}
return;
}
down(k);
int mid = (c[k].l + c[k].r) >> 1;
if (l <= mid)update(l, r, k << 1, d);
if (r > mid)update(l, r, k << 1 | 1, d);
c[k].sum = c[k << 1].sum + c[k << 1 | 1].sum;
}
int getAns(int x, int y) {
int ans = 0;
while (top[x] != top[y]) {
if (depth[top[x]] < depth[top[y]]) {
swap(x, y);
}
ans += query(dfn[top[x]], dfn[x], 1);
x = fa[top[x]];
}
if (depth[x] < depth[y])swap(x, y);
ans += query(dfn[y], dfn[x], 1);
return ans;
}
void change(int x, int y, int d) {
while (top[x] != top[y]) {
if (depth[top[x]] < depth[top[y]]) {
swap(x, y);
}
update(dfn[top[x]], dfn[x], 1, d);
x = fa[top[x]];
}
if (depth[x] < depth[y])swap(x, y);
update(dfn[y], dfn[x], 1, d);
}
} ST;
ST st;
int n, Q;
int main()
{
freopen("in.txt", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
init();
cin >> n;
for (int i = 1; i < n; i++) {
int x;
cin >> x;
add(i, x);
add(x, i);
}
dfs1(0, 1);
dfs2(0, 0);
st.build(1, num, 1);
cin >> Q;
char op[12];
int po;
int ans;
while (Q--) {
cin >> op >> po;
if (op[0] == 'i') {
ans = st.getAns(po, 0);
cout << ans << "\n";
st.change(po, 0, 1);
}
else {
ans = st.query(dfn[po], dfn[po] + siz[po] - 1, 1);
cout << siz[po] - ans << "\n";
st.update(dfn[po], dfn[po] + siz[po] - 1, 1, -1);
}
}
return 0;
}
本文来自博客园,作者:correct,转载请注明原文链接:https://www.cnblogs.com/correct/p/12861901.html

浙公网安备 33010602011771号