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 ) 个软件包 A 1 , A 2 , A 3 , … , A m A1,A2,A3,…,Am ,其中 A 1 A1 依赖 A 2 A2 ,A 2 A2 依赖 A 3 A3 ,A 3 A3 依赖 A 4 A4 ,……,A m − 1 Am−1 依赖 A m Am ,而 A m Am 依赖 A 1 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 步操作中改变安装状态的软件包数。
样例一
7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0
output
3
1
3
2
3
explanation
一开始所有的软件包都处于未安装状态。
安装 5 5 号软件包,需要安装 0 , 1 , 5 0 , 1 , 5 三个软件包。
之后安装 6 6 号软件包,只需要安装 6 6 号软件包。此时安装了 0 , 1 , 5 , 6 0,1,5,6 四个软件包。
卸载 1 1 号软件包需要卸载 1 , 5 , 6 1 , 5 , 6 三个软件包。此时只有 0 0 号软件包还处于安装状态。
之后安装 4 4 号软件包,需要安装 1 , 4 1,4 两个软件包。此时 0 , 1 , 4 0,1,4 处在安装状态。
最后,卸载 0 0 号软件包会卸载所有的软件包。
样例二
10
0 1 2 1 3 0 0 3 2
10
install 0
install 3
uninstall 2
install 7
install 5
install 9
uninstall 9
install 4
install 1
install 9
output
1
3
2
1
3
1
1
1
0
1
样例三
见样例数据下载。
限制与约定
测试点编号 n n 的规模q q 的规模备注 1 n = 5000 n = 5000 q = 5000 q=5000 2 3 n = 100000 n=100000 q = 100000 q=100000 数据不包含卸载操作 4 5 n = 100000 n=100000 q = 100000 q=100000 编号为 i i 的软件包所依赖的软件包编号在 [ 0 , i − 1 ] [ 0 , i − 1 ] 内均匀随机。 每次执行操作的软件包编号在 [ 0 , n − 1 ] [0,n−1] 内均匀随机。 6 7 8 9 n = 100000 n=100000 q = 100000 q=100000 10 11 12 13 14 15 16 17 18 19 20
时间限制: 1 s 1s
空间限制: 512 MB 512MB
思路&&分析
这题,我们用0表示一个软件没有被下载,1表示已经被下载。这题一共有两个操作,我们很容易看出操作uninstall x是把以x为根的子树的所有已经install过的软件给卸载掉,就是把x和他子树的值全都赋为0;而操作install x则可以看做是把从根到x的路径上的所有软件包(包括x)全都给下载下来,就是对一条路径上的所有点赋值1。简要分析过后我们就可以发现这是道树剖裸题,只要用线段树维护所有的值就行了。对于答案,我们在每次修改前记录一下根的sum,然后sum与操作后根的sum的绝对值就是答案了。
Code
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll;
template <class T>inline void read(T &x) {
x=0 ;T f=1 ;char ch=getchar();
while (!isdigit (ch)){if (ch=='-' )f=-1 ;ch=getchar();}
while (isdigit (ch))x=(x<<1 )+(x<<3 )+ch-'0' ,ch=getchar();
x*=f;
}
#define rd(x) read(x)
#define r2(x,y) rd(x),rd(y)
#define r3(x,y,z) r2(x,y),rd(z)
#define r4(x,y,z,o) r2(x,y),r2(z,o)
#define ls o<<1
#define rs o<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int maxn=1000010 ;
int n,q,fa[maxn],dep[maxn],tp[maxn],son[maxn],id[maxn],dfs_clock,sz[maxn];
vector <int > G[maxn];
struct Seg {
int sum,upd;
inline void init() {
sum=0 ;
upd=-1 ;
}
}t[maxn<<2 ];
inline void pushup(int o) {
t[o].sum=t[ls].sum+t[rs].sum;
}
inline void pushdown(int o,int l,int r) {
if (t[o].upd!=-1 ) {
int mid=(l+r)>>1 ;
t[ls].sum=t[o].upd*(mid-l+1 );
t[rs].sum=t[o].upd*(r-mid);
t[ls].upd=t[rs].upd=t[o].upd;
t[o].upd=-1 ;
}
}
inline void dfs1(int u) {
sz[u]=1 ;
for (unsigned i=0 ;i<G[u].size();i++) {
int v=G[u][i];
if (v==fa[u])
continue ;
fa[v]=u;
dep[v]=dep[u]+1 ;
dfs1(v);
sz[u]+=sz[v];
if (!son[u]||sz[son[u]]<sz[v])
son[u]=v;
}
}
inline void dfs2(int u,int p) {
id[u]=++dfs_clock;
tp[u]=p;
if (!son[u])
return ;
dfs2(son[u],p);
for (unsigned i=0 ;i<G[u].size();i++) {
int v=G[u][i];
if (v==fa[u]||v==son[u])
continue ;
dfs2(v,v);
}
}
inline void build(int o,int l,int r) {
t[o].init();
if (l==r)
return ;
int mid=(l+r)>>1 ;
build(lson);
build(rson);
pushup(o);
}
inline void upset(int o,int l,int r,int ql,int qr,int s) {
pushdown(o,l,r);
if (l==ql&&r==qr) {
t[o].sum=s*(r-l+1 );
t[o].upd=s;
return ;
}
int mid=(l+r)>>1 ;
if (qr<=mid)
upset(lson,ql,qr,s);
else if (ql>mid)
upset(rson,ql,qr,s);
else {
upset(lson,ql,mid,s);
upset(rson,mid+1 ,qr,s);
}
pushup(o);
}
inline void subset1(int x) {
upset(1 ,1 ,n,id[x],id[x]+sz[x]-1 ,0 );
}
inline void subset2(int x) {
while (tp[x]!=1 ) {
upset(1 ,1 ,n,id[tp[x]],id[x],1 );
x=fa[tp[x]];
}
upset(1 ,1 ,n,1 ,id[x],1 );
}
int main() {
rd(n);
for (int i=2 ,x;i<=n;i++) {
rd(x);x++;
G[x].push_back(i);
G[i].push_back(x);
}
dep[1 ]=1 ;fa[1 ]=-1 ;
dfs1(1 );
dfs2(1 ,1 );
build(1 ,1 ,n);
rd(q);
while (q--) {
char s[100 ];int x;
scanf ("%s" ,s+1 );rd(x);x++;
int res=t[1 ].sum;
if (s[1 ]=='u' ) {
subset1(x);
printf ("%d\n" ,abs (res-t[1 ].sum));
}
else {
subset2(x);
printf ("%d\n" ,abs (res-t[1 ].sum));
}
}
}
确定要放弃本次机会?
福利倒计时
:
:
立减 ¥
普通VIP年卡可用
立即使用
02-04
1079
05-13
362
题目
细节过多。
一对路径相交当且仅当一个的lcalcalca在另一条路径上。
所以我们如果询问一条路径相交的其他路径的权值和。
把其他路径p=( u,v) p=( u,v) p=( u,v) 的lcalcalca的vala+ =wpval_a+ =w_pvala+ =wp,[u,lca) 和( lca,v]的valb+ =wp[u,lca) 和( lca,v]的val_b + = w_p[u,lca) 和( lca,v]的valb+ =wp
那么询问路径( x,y) ( x,y) ( x,y) 的权值和就是valb,lca( x,y) + vala,
目录
展开全部
收起
目录
展开全部
收起
成就一亿技术人!