【数链剖分】BZOJ4196

【题目大意】

Linux用户和OSX用户一定对软件包管理器不会陌生。通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置。Debian/Ubuntu使用的apt-get,Fedora/CentOS使用的yum,以及OSX下可用的homebrew都是优秀的软件包管理器。

你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包A依赖软件包B,那么安装软件包A以前,必须先安装软件包B。同时,如果想要卸载软件包B,则必须卸载软件包A。现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除0号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而0号软件包不依赖任何一个软件包。依赖关系不存在环(若有m(m≥2)个软件包A1,A2,A3,…,Am,其中A1依赖A2,A2依赖A3,A3依赖A4,……,Am−1依赖Am,而Am依赖A1,则称这m个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己。

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

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 #define rep(i,l,r) for(int i=l; i<=r; i++)
  6 #define clr(x,y) memset(x,y,sizeof(x))
  7 #define travel(x) for(Edge *p=last[x]; p; p=p->pre)
  8 using namespace std;
  9 const int INF = 0x3f3f3f3f;
 10 const int maxn = 100010;
 11 inline int read(){
 12     int ans = 0, f = 1;
 13     char c = getchar();
 14     for(; !isdigit(c); c = getchar())
 15     if (c == ‘-‘) f = -1;
 16     for(; isdigit(c); c = getchar())
 17     ans = ans * 10 + c - ‘0‘;
 18     return ans * f;
 19 }
 20 struct Edge{
 21     Edge *pre; int to;
 22 }edge[maxn<<1];
 23 Edge *last[maxn], *pt = edge;
 24 struct Node{
 25     int l,r,v,t;
 26 }t[maxn<<2];
 27 int n,m,x,y,q,segnum=0,dep[maxn],top[maxn],size[maxn],fa[maxn],pos[maxn];
 28 char ch[10];
 29 inline void addedge(int x,int y){
 30     pt->pre = last[x]; pt->to = y; last[x] = pt++;
 31 }
 32 void dfs(int x){
 33     size[x] = 1;
 34     travel(x){
 35         dep[p->to] = dep[x] + 1;
 36         fa[p->to] = x;
 37         dfs(p->to);
 38         size[x] += size[p->to];
 39     }
 40 }
 41 void dfs(int x,int chain){
 42     pos[x] = ++segnum; top[x] = chain;
 43     int k = 0;
 44     travel(x){
 45         if (size[p->to] > size[k]) k = p->to;
 46     }
 47     if (!k) return;
 48     dfs(k,chain);
 49     travel(x){
 50         if (p->to != k) dfs(p->to,p->to);
 51     }
 52 }
 53 void build(int u,int v,int w){
 54     t[w].l = u; t[w].r = v; t[w].v = 0; t[w].t = -1;
 55     if (u == v) return;
 56     int mid = (u + v) >> 1;
 57     build(u,mid,w<<1); build(mid+1,v,w<<1|1);
 58 }
 59 inline void pushdown(int w){
 60     if (t[w].l == t[w].r || t[w].t == -1) return;
 61     t[w<<1].t = t[w<<1|1].t = t[w].t;
 62     t[w<<1].v = (t[w<<1].r - t[w<<1].l + 1) * t[w].t;
 63     t[w<<1|1].v = (t[w<<1|1].r - t[w<<1|1].l + 1) * t[w].t;
 64     t[w].t = -1;
 65 }
 66 inline void maintain(int w){
 67     t[w].v = t[w<<1].v + t[w<<1|1].v;
 68 }
 69 void modify(int u,int v,int w,int x){
 70     pushdown(w);
 71     if (t[w].l == u && t[w].r == v){
 72         t[w].v = x * (v - u + 1);
 73         t[w].t = x; return;
 74     }
 75     int mid = (t[w].l + t[w].r) >> 1;
 76     if (v <= mid) modify(u,v,w<<1,x);
 77     else if (u > mid) modify(u,v,w<<1|1,x);
 78     else{
 79         modify(u,mid,w<<1,x);
 80         modify(mid+1,v,w<<1|1,x);
 81     }
 82     maintain(w);
 83 }
 84 int query(int u,int v,int w){
 85     pushdown(w);
 86     if (t[w].l == u && t[w].r == v) return t[w].v;
 87     int mid = (t[w].l + t[w].r) >> 1;
 88     if (v <= mid) return query(u,v,w<<1);
 89     else if (u > mid) return query(u,v,w<<1|1);
 90     else return query(u,mid,w<<1) + query(mid+1,v,w<<1|1);
 91 }
 92 int install(int x,int y){
 93     int ret = 0;
 94     while (top[x] != top[y]){
 95         if (dep[top[x]] < dep[top[y]]) swap(x,y);
 96         ret += query(pos[top[x]],pos[x],1);
 97         modify(pos[top[x]],pos[x],1,1);
 98         x = fa[top[x]];
 99     }
100     if (dep[x] < dep[y]) swap(x,y);
101     ret += query(pos[y],pos[x],1);
102     modify(pos[y],pos[x],1,1);
103     return ret;
104 }
105 int uninstall(int x){
106     int ret = query(pos[x],pos[x]+size[x]-1,1);
107     modify(pos[x],pos[x]+size[x]-1,1,0);
108     return ret;
109 }
110 int main(){
111     n = read(); clr(last,0);
112     rep(i,1,n-1){
113         x = read();
114         addedge(x+1,i+1);
115     }
116     dep[1] = 0; dfs(1); dfs(1,1); build(1,n,1);
117     q = read();
118     rep(i,1,q){
119         scanf("%s",ch); x = read() + 1;
120         if (ch[0] == ‘i‘) printf("%d\n",dep[x] + 1 - install(1,x));
121         else printf("%d\n",uninstall(x));
122     }
123     return 0;
124 }
时间: 2024-10-18 19:11:37

【数链剖分】BZOJ4196的相关文章

数链剖分小结

留题目占地..

浅谈树链剖分(C++、算法、树结构)

关于数链剖分我在网上看到的有几个比较好的讲解,本篇主要是对AC代码的注释(感谢各位witer的提供) 这是讲解 http://www.cnblogs.com/kuangbin/archive/2013/08/15/3259083.html 另一个是百度文库 http://wenku.baidu.com/link?url=DY8CAbwdjitIiv8XQsHmVPi--dQAqw5z6dc_6N1Plh4u5Nfc1aCADQm4oAvt4Sqe1mXSixezzK4lRxofQKMX9cNzJ

【不可能的任务3/200】bzoj2243树链剖分+染色段数

终于做了一道不是一眼出思路的代码题(⊙o⊙) 之前没有接触过这种关于染色段数的题目(其实上课好像讲过),于是百度了一下(现在思维能力好弱) 实际上每一段有用的信息就是总共有几段和两段各是什么颜色,在开线段树的时候记录一下就好了 事实上我开了一个node,并且写了一个mix还是大大减小了代码量(对于我这种手残党来说同时大大减小了错误率) 由于前几题做的都是树链剖分,并没有在这一方面出问题,然而线段树还是不熟练,刚写完的时候居然忘记down了(mdzz) 对wa了N遍的总结: 由于是树上两个点间的路

HYSBZ - 2243 树链剖分 + 线段树 处理树上颜色段数

用线段树处理颜色段数 记录区间内的颜色段数,区间右端点的颜色,区间右端点的颜色. int tr[maxn<<2], lc[maxn<<2], rc[maxn<<2]; 懒标记,记录区间是否被覆盖 int lazy[maxn<<2]; 合并的方法是这样,对于某一区间 ? 如果 左区间的右端点颜色 == 右区间的左端点 ? 那么 这左右区间合并,左区间的最右边一段和右区间最左边一段颜色是连续的,那么区间的颜色段数为 左区间颜色段数+右区间颜色段数 - 1. ?

[Bzoj4196] [NOI2015] 软件包管理器 [树链剖分,线段树]

题解摘要:树链剖分后用线段树区间查询修改,对于安装软件,将改点到根的路径全部变为1,对于卸载软件,将子树清空.注意边界,编号是从0开始的,容易漏掉树根. 第一次写树剖- 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cmath> 7 #inclu

bzoj4196 [Noi2015]软件包管理器——树链剖分

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4196 树链剖分. 代码如下: #include<iostream> #include<cstdio> #include<cstring> using namespace std; int const maxn=1e5+5; int n,m,fa[maxn],dfn[maxn],end[maxn],top[maxn],to[maxn],siz[maxn],head[

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

bzoj 2243: [SDOI2011]染色 线段树区间合并+树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 7925  Solved: 2975[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完

bzoj3694: 最短路(树链剖分/并查集)

bzoj1576的帮我们跑好最短路版本23333(双倍经验!嘿嘿嘿 这题可以用树链剖分或并查集写.树链剖分非常显然,并查集的写法比较妙,涨了个姿势,原来并查集的路径压缩还能这么用... 首先对于不在最短路径树上的边x->y,设t为最短路径树上lca(x,y),则t到y上的路径上的点i到根的距离都可以用h[x]+dis[x][y]+h[y]-h[i](h[]为深度)来更新,因为h[i]一定,只要让h[x]+dis[x][y]+h[y]最小就行,这里用树剖直接修改整条链上的数,就可以过了. 并查集的