bzoj4196: [Noi2015]软件包管理器(树链剖分)
太久没写树剖都忘了,赶紧来写写。
题目描述:有n个软件,每个软件都有一个且仅有一个依赖关系,只有安装了依赖的软件才能安装这个软件。0节点没有依赖关系。
给定q个询问,每个询问为安装或卸载某个软件,问每次询问会改变多少个软件的安装状态。
输入格式:第一行一个整数表示n。
第二行第i个表示第i个软件的依赖软件。
第三行一个整数q,表示询问的个数。
接下来q行每行一个询问,install x 表示安装编号为 x 的软件。uninstall x 表示卸载编号为 x 的软件。
输出格式:每个询问,输出一个整数,表示会改变的软件状态。
输入样例:
7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0
输出样例:
3
1
3
2
3
解析:一道树剖的模板题,用0表示没有安装,用1表示安装了。每次安装操作就输出dep[x] - x到根节点的1的个数,再将根节点到 x 变为1。
卸载时就询问 x 的子树中1的个数,再全部改为0即可。因为 x 的子树中的dfs序是连续的,所以直接用子树的大小就可以知道在线段树上的标号。
代码如下:
1 #include<cstdio> 2 #include<vector> 3 #include<algorithm> 4 #include<cstring> 5 #define lc o << 1 6 #define rc o << 1 | 1 7 using namespace std; 8 9 const int maxn = 1e5 + 5; 10 int n, bj[maxn * 4], sum[maxn * 4], q; 11 int dfn[maxn], cnt, dep[maxn], fa[maxn], seq[maxn], size[maxn], heavy[maxn], top[maxn]; 12 char s[15]; 13 vector <int> ve[maxn]; 14 15 int read(void) { 16 char c; while (c = getchar(), c < '0' || c >'9'); int x = c - '0'; 17 while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x; 18 } 19 20 void dfs1(int u, int pre) { 21 dep[u] = dep[pre] + 1; 22 fa[u] = pre; size[u] = 1; 23 for (int i = 0; i < ve[u].size(); ++ i) { 24 int v = ve[u][i]; 25 if (v == pre) continue; 26 dfs1(v, u); 27 size[u] += size[v]; 28 if (size[v] > size[heavy[u]]) heavy[u] = v; 29 } 30 } 31 32 void dfs2(int u, int cur) { 33 top[u] = cur; dfn[u] = ++ cnt; 34 seq[cnt] = u; //seq对这题好像没什么用,但还是打了 QAQ 35 if (!heavy[u]) return; 36 dfs2(heavy[u], cur); 37 for (int i = 0; i < ve[u].size(); ++ i) { 38 int v = ve[u][i]; 39 if (v == fa[u] || v == heavy[u]) continue; 40 dfs2(v, v); 41 } 42 } 43 44 void pushdown(int o, int l, int r) { 45 int mid = l + r >> 1; 46 sum[lc] = bj[o] * (mid - l + 1); 47 sum[rc] = bj[o] * (r - mid); 48 bj[lc] = bj[rc] = bj[o]; bj[o] = -1; 49 } 50 51 void modify(int o, int l, int r, int ql, int qr, int c) { 52 if (ql <= l && qr >= r) { 53 sum[o] = c * (r - l + 1); bj[o] = c; 54 return; 55 } 56 int mid = l + r >> 1; 57 if (bj[o] != -1) pushdown(o, l, r); 58 if (ql <= mid) modify(lc, l, mid, ql, qr, c); 59 if (qr > mid) modify(rc, mid + 1, r, ql, qr, c); 60 sum[o] = sum[lc] + sum[rc]; 61 } 62 63 int query(int o, int l, int r, int ql, int qr) { 64 if (ql <= l && qr >= r) return sum[o]; 65 int mid = l + r >> 1, ans = 0; 66 if (bj[o] != -1) pushdown(o, l, r); 67 if (ql <= mid) ans += query(lc, l, mid, ql, qr); 68 if (qr > mid) ans += query(rc, mid + 1, r, ql, qr); 69 return ans; 70 } 71 72 void chain_modify(int x, int y, int c) { 73 int fax = top[x], fay = top[y]; 74 while (fax != fay) { 75 if (dep[fax] < dep[fay]) { 76 swap(fax, fay); 77 swap(x, y); 78 } 79 modify(1, 1, n, dfn[fax], dfn[x], c); 80 x = fa[fax]; 81 fax = top[x]; 82 } 83 if (dep[x] > dep[y]) swap(x, y); 84 modify(1, 1, n, dfn[x], dfn[y], c); 85 } 86 87 int chain_query(int x, int y) { 88 int fax = top[x], fay = top[y], ans = 0; 89 while (fax != fay) { 90 if (dep[fax] < dep[fay]) { 91 swap(fax, fay); 92 swap(x, y); 93 } 94 ans += query(1, 1, n, dfn[fax], dfn[x]); 95 x = fa[fax]; 96 fax = top[x]; 97 } 98 if (dep[x] > dep[y]) swap(x, y); 99 ans += query(1, 1, n, dfn[x], dfn[y]); 100 return ans; 101 } 102 103 int main() { 104 n = read(); 105 for (int i = 2; i <= n; ++ i) { 106 int x = read() + 1; 107 ve[x].push_back(i); 108 ve[i].push_back(x); 109 } 110 q = read(); 111 dfs1(1, 0); 112 dfs2(1, 1); 113 memset(bj, -1, sizeof(bj)); 114 while (q --) { 115 scanf("%s", s + 1); 116 int x = read() + 1; 117 if (s[1] == 'i') { 118 int ans = dep[x] - chain_query(1, x); 119 if (ans < 0) { 120 ans = ans; 121 ans = chain_query(1, x); 122 } 123 printf("%d\n", dep[x] - chain_query(1, x)); 124 chain_modify(1, x, 1); 125 } 126 else { 127 printf("%d\n", query(1, 1, n, dfn[x], dfn[x] + size[x] - 1)); //直接用size算出子树中节点的dfs序最大值 128 modify(1, 1, n, dfn[x], dfn[x] + size[x] - 1, 0); 129 } 130 } 131 return 0; 132 }