洛谷P2146 [NOI2015]软件包管理器 (树链剖分)

题目描述

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

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

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

输入格式:

输入文件的第1行包含1个整数 n n n,表示软件包的总数。软件包从 0 0 0开始编号。

随后一行包含 n − 1 n−1 n1个整数,相邻整数之间用单个空格隔开,分别表示 1 , 2 , 3 , ⋯ , n − 2 , n − 1 1,2,3,⋯,n−2,n−1 1,2,3,,n2,n1号软件包依赖的软件包的编号。

接下来一行包含1个整数 q q q,表示询问的总数。之后q行,每行1个询问。询问分为两种:

i n s t a l l x install x installx:表示安装软件包 x x x

u n i n s t a l l x uninstall x uninstallx:表示卸载软件包 x x x

你需要维护每个软件包的安装状态,一开始所有的软件包都处于未安装状态。

对于每个操作,你需要输出这步操作会改变多少个软件包的安装状态,随后应用这个操作(即改变你维护的安装状态)。

输出格式:

输出文件包括 q q q行。

输出文件的第i行输出1个整数,为第i步操作中改变安装状态的软件包数。

输入样例#1:

7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0

输出样例#1:

3
1
3
2
3

输入样例#2:

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

输出样例#2:

1
3
2
1
3
1
1
1
0
1

这个题是一个树链剖分的模板题。

首先看题目给的信息: N N N个 点 , N − 1 N - 1 N1 条边 ,且每个点之间都联通。所以我们可以断定这是一棵树 (废话)。

这个题让我们维护两种操作 :install (安装) 和 uninstall(卸载)。除了根节点,每一个软件都会依赖另一个软件。且(A依赖B ,B依赖C ,则A依赖C)。安装某一个软件的时候要把他所有的依赖的软件都装上,卸载时则要把依赖他的软件都卸载。

因为涉及到树上的修改和查询操作,所以我们用树链剖分。

我们设 1 1 1 为安装了这个软件, 0 0 0 为未安装这个软件。

因为每个节点的依赖关系一定会延伸到根节点,所以安装时我们修改这个点到跟节点这个路径,全部改为 1 1 1。 因为每个节点的子树里的所有点一定依赖这个节点,所以我们直接将这颗子树改为 0 0 0 就ok了。
这样我们就可以正常进行树链剖分了。

#include<bits/stdc++.h>

using namespace std;

const int maxn = 120000;
#define ll long long
#define ls now<<1
#define rs now<<1|1
struct node
{
	ll f, t, v;
}e[maxn<<1];
struct tree
{
	ll l, r, add, sum, num;
}tre[maxn<<2];
ll n, m, tot , a , cnt;
ll head[maxn], nxt[maxn << 1], sz[maxn], top[maxn], fa[maxn];
ll dep[maxn], son[maxn] , num[maxn];
inline ll read()
{
	ll x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
	return x * f;
}
inline void buildnode(int a, int b)
{
	e[++tot] = (node){a , b};
	nxt[tot] = head[a];
	head[a] = tot;
}
inline void dfs1(ll x, ll fat)
{
	dep[x] = dep[fat] + 1;
	sz[x] = 1;
	fa[x] = fat;
	int k = -1;
	for (int i = head[x]; i; i = nxt[i])
	{
		int u = e[i].t;
		if (u == fa[x]) continue;
		dfs1(u, x);
		sz[x] += sz[u];
		if (sz[u] > k) k = sz[u], son[x] = u;
	}
}
inline void dfs2(ll x , ll topx)
{
	num[x] = ++cnt;
	top[x] = topx;
	if (!son[x]) return;
	dfs2(son[x], topx);
	for (int i = head[x]; i; i = nxt[i])
	{
		int u = e[i].t;
		if (u == fa[x] || u == son[x]) continue;
		dfs2(u, u);
	}
}
inline void pushup(ll now)
{
	tre[now].sum = tre[ls].sum + tre[rs].sum;
}
inline void build(ll now, ll l, ll r)
{
	tre[now].l = l;
	tre[now].r = r;
	tre[now].num = r - l + 1;
	if (l == r) return;
	ll mid = (l + r) >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	pushup(now);
}
inline void pushdown(ll now)
{
	if (tre[now].add)
	{
		if (tre[now].add == 1)
		{
			tre[ls].sum = tre[ls].num;
			tre[rs].sum = tre[rs].num;
		}
		else tre[ls].sum = tre[rs].sum = 0;
		tre[ls].add = tre[rs].add = tre[now].add;
	}
	tre[now].add = 0;
}
inline void update(ll now, ll l, ll r, ll p)
{
	if (l <= tre[now].l && tre[now].r <= r)
	{
		if (p == 0) tre[now].sum = 0;
		else tre[now].sum = tre[now].num;
		tre[now].add = 2 - p; //2为变成0 , 1为变成1
		return;
	}
	pushdown(now);
	ll mid = (tre[now].l + tre[now].r) >> 1;
	if (l <= mid) update(ls, l, r, p);
	if (r > mid) update(rs, l, r, p);
	pushup(now);
}
inline ll query(ll now, ll l, ll r)
{
	if (l <= tre[now].l && tre[now].r <= r)
		return tre[now].sum;
	pushdown(now);
	ll mid = (tre[now].l + tre[now].r) >> 1, ans = 0;
	if (l <= mid) ans += query(ls, l, r);
	if (r > mid) ans += query(rs, l, r);
	return ans;
}
inline ll qpoint(ll x)
{
	ll ans = 0;
	while (top[x] != 1)
	{
		ans += query(1, num[top[x]], num[x]);
		update(1, num[top[x]], num[x], 1);
		x = fa[top[x]];
	}
	ans += query(1, 1, num[x]);
	update(1, 1, num[x], 1);
	return ans;
}
int main()
{
	n = read();
	for (int i = 2; i <= n; i++)
	{
		a = read();
		buildnode(i, a + 1), buildnode(a + 1, i);
	}
	dfs1(1, 0); dfs2(1, 1);
	build(1, 1, n);
	m = read();
	string s1 = "install", s2 = "uninstall" , s;
	for (int i = 1; i <= m; i++)
	{
		cin >> s;
		a = read();
		a += 1;
		if (s == s1) 
		{
			printf("%lld\n", dep[a] - qpoint(a));
		}
		if (s == s2)
		{
			printf("%lld\n", query(1, num[a], num[a] + sz[a] - 1));
			update(1, num[a], num[a] + sz[a] - 1, 0);
		}
	}
//	system("pause");
}

End

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值