Linux 用户和 OS X 用户一定对软件包管理器不会陌生。通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置。Debian/Ubuntu 使用的 apt-get,Fedora/CentOS 使用的 yum,以及 OS X 下可用的 homebrew 都是优秀的软件包管理器。
你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包 A A 依赖软件包 B B,那么安装软件包 A A 以前,必须先安装软件包 B B。同时,如果想要卸载软件包 B B,则必须卸载软件包 A A。现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除 0 0 号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而 0 0 号软件包不依赖任何一个软件包。依赖关系不存在环(若有 m m (m≥2) (m≥2) 个软件包 A1,A2,A3,…,Am A1,A2,A3,…,Am,其中 A1 A1 依赖 A2 A2, A2 A2 依赖 A3 A3, A3 A3 依赖 A4 A4,……, Am−1 Am−1 依赖 Am Am,而 Am Am 依赖 A1 A1,则称这 m m 个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己。
现在你要为你的软件包管理器写一个依赖解决程序。根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为 0 0。
输入格式
输入文件的第 1 1 行包含 1 1 个正整数 n n,表示软件包的总数。软件包从 0 0 开始编号。
随后一行包含 n−1 n−1 个整数,相邻整数之间用单个空格隔开,分别表示 1,2,3,…,n−2,n−1 1,2,3,…,n−2,n−1 号软件包依赖的软件包的编号。
接下来一行包含 1 1 个正整数 q q,表示询问的总数。
之后 q q 行,每行 1 1 个询问。询问分为两种:
- install x x:表示安装软件包 x x
- uninstall x x:表示卸载软件包 x x
你需要维护每个软件包的安装状态,一开始所有的软件包都处于未安装状态。对于每个操作,你需要输出这步操作会改变多少个软件包的安装状态,随后应用这个操作(即改变你维护的安装状态)。
输出格式
输出文件包括 q q 行。
输出文件的第 i i 行输出 1 1 个整数,为第 i i 步操作中改变安装状态的软件包数。
input
7 0 0 0 1 1 5 5 install 5 install 6 uninstall 1 install 4 uninstall 0
output
3 1 3 2 3
蒟蒻的第一道树剖题。
每个软件只有一个依赖的软件,所以数据显然是一棵树。期中0号软件是Root.
安装软件就是将该点向上直至0节点全由0变成1,删除则是将一个子树全改成0.
一波树链剖分,按照重链优先的原则求dfs序,则可以将树状的结构转化为线性的数组(pos),且在一条重链上的各个点的pos是连续的。这样,将pos作为一个线段树的基础,那就可以通过这棵线段树来对一个区间(即一条重链)操作。
top[x]表示x所在重链的链顶。
pos[x]表示x在线段树最低一层的位置。
fa[x]表示x的父亲,即x软件直接依赖的软件的编号。
sons[x]表示以x为根的子树的节点数。
hson[x]表示x最重的儿子的编号。
gh[x]中存x的所有儿子。
uninstall时,ans=itree[now].sum,并将itree[now]的lazy值标记为0(全删)。
install,top[x]到x(即x所在重链中x及其之上的部分)全部改为1,则线段树中pos[top[x]]至pos[x]改为1,且ans+=itree[now].r-itree[now].l+1-itree[now].sum;再将x=fa[top[x]](即沿轻链向上走一步,找到下一个重链),直至x为0节点。
然后就可以搞了。然而我照着老师的代码改了两个半点。果然第一次写还是不要原创代码了。
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<vector>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;
inline int read(){
char c;int ret=0;
do{c=getchar();}while(c>'9'||c<'0');
do{ret=ret*10+c-48;c=getchar();}while(c>='0'&&c<='9');
return ret;
}
struct node{
int l,r,sum,ch;
};
const int man=100000;
int n,q;
int fa[man+10];
int sons[man+10],hson[man+10],pos[man+10],top[man+10];
vector<int> gh[man+10];
node itree[man*3+10];
int treecnt=0;
void input(){
n=read();
for(int i=1;i<=n-1;i++)
{
int t;
t=read();
fa[i]=t;
gh[t].push_back(i);
}
}
int dfs1(int x){
//ruturn值为x为根节点的子树的节点个数
int ha=1,bigone=0,big=0;//bigone是x最重的儿子,big是此儿子的重量
for(int i=0;i<gh[x].size();i++)
{
int a=dfs1(gh[x][i]);
ha+=a;
if(a>big)
{
big=a;
bigone=gh[x][i];
}
}
sons[x]=ha;
hson[x]=bigone;
return ha;
}
int dfs2(int now) {
treecnt++;
pos[now] = treecnt;
if (sons[now] == 1) return 0;
top[hson[now]] = top[now];
dfs2(hson[now]);
for (int i = 0; i < gh[now].size(); i++)
if (gh[now][i] != hson[now]) {
top[gh[now][i]] = gh[now][i];
dfs2(gh[now][i]);
}
return 0;
}
void build(){
//先建立线段树的最底一层,然后由下至上建立线段树
int k;
for( k = 1 ; k < n ; k <<= 1 );
n=k;
for(int i=1;i<=n;i++)
{
itree[i+n-1].l=itree[i+n-1].r=i;
itree[i+n-1].sum=0;
itree[i+n-1].ch=2;
}
for(int i=n-1;i>=1;i--)
{
itree[i].l=itree[i*2].l;
itree[i].r=itree[i*2+1].r;
itree[i].sum=0;
itree[i].ch=2;
}
return;
}
void init(){
fa[0]=-1;
dfs1(0);
dfs2(0);
build();
}
void push(int now){
if(itree[now].ch==2)return;
int lc=now*2;
int rc=lc+1;
itree[lc].ch=itree[now].ch;
itree[rc].ch=itree[now].ch;
itree[lc].sum=itree[lc].ch*(itree[lc].r-itree[lc].l+1);
itree[rc].sum=itree[rc].ch*(itree[rc].r-itree[rc].l+1);
itree[now].ch=2;
}
int add(int now,int x,int y,int op){
int ret=0;
if(itree[now].l>=x&&itree[now].r<=y)
{
if(op==1)ret=itree[now].r-itree[now].l+1-itree[now].sum;
else ret=itree[now].sum;
itree[now].ch=op;
itree[now].sum=(itree[now].r-itree[now].l+1)*op;
return ret;
}
push(now);
if(itree[now*2].r>=x)ret+=add(now*2,x,y,op);
if(itree[now*2+1].l<=y)ret+=add(now*2+1,x,y,op);
itree[now].sum=itree[now*2].sum+itree[now*2+1].sum;
return ret;
}
int install(int x){
int ret=0;
while(x!=-1)
{
ret+=add(1,pos[top[x]],pos[x],1);
x=fa[top[x]];
}
return ret;
}
int uninstall(int x){
return add(1,pos[x],pos[x]+sons[x]-1,0);
}
int main(){
input();
init();
q=read();
for(int i=1;i<=q;i++)
{
int t;
char opp[15];
scanf("%s%d",opp,&t);
if(opp[0]=='i')
printf("%d\n",install(t));
else
printf("%d\n",uninstall(t));
}
return 0;
}
LLAP.