P2146 [NOI2015]软件包管理器 线段树 + 树链剖分

9 篇文章 0 订阅
6 篇文章 0 订阅

传送门

思路:树链剖分模板题,唯一需要想一下的是在安装软件包时如何求得该点到根节点路径上的节点数,不难想出,我们只需要在找LCA时用一个变量记录即可。

代码:

#include <iostream>
#include <string>

using namespace std;

const int  N = 1e5 + 10;

struct edge
{
	int to, next;
}Edge[N<<1];

int n, cnt, head[N], sum[N<<2], lazy[N<<2], num = 0;
int f[N], son[N], id[N], rk[N], dep[N], size[N], top[N];

void add_edge(int from,int to)
{
	Edge[++cnt].to = to;
	Edge[cnt].next = head[from];
	head[from] = cnt;
}

void dfs1(int v,int fa,int depth)
{
	size[v] = 1;
	dep[v] = depth;
	f[v] = fa;
	for(int i = head[v]; i; i = Edge[i].next){
		int to = Edge[i].to;
		if(to == fa) continue;
		dfs1(to,v,depth+1);
		size[v] += size[to];
		if(size[to] > size[son[v]])
			son[v] = to;
	}
}

void dfs2(int v,int tp)
{
	top[v] = tp;
	id[v] = ++cnt;
	rk[cnt] = v;
	if(!son[v]) return ;
	dfs2(son[v],tp);
	for(int i = head[v]; i; i = Edge[i].next){
		int to = Edge[i].to;
		if(to == son[v] || to == f[v]) continue;
		dfs2(to,to);
	}
}

void pushUp(int rt)
{
	sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void pushDown(int rt,int l,int r)
{
	if(lazy[rt] == -1) return ;
	int m = (l+r)>>1;
	sum[rt<<1] = (m-l+1) * lazy[rt], sum[rt<<1|1] = (r-m) * lazy[rt];
	lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
	lazy[rt] = -1;
}

void build(int l,int r,int rt)
{
	sum[rt] = 0;
	lazy[rt] = -1;
	if(l == r) return ;
	int m = (l+r)>>1;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
}

void update(int L,int R,int C,int l,int r,int rt)
{
	if(L <= l && r <= R){
		sum[rt] = (r-l+1) * C;
		lazy[rt] = C;
		return ;
	}
	int m = (l+r)>>1;
	pushDown(rt,l,r);
	if(L <= m) update(L,R,C,l,m,rt<<1);
	if(m <  R) update(L,R,C,m+1,r,rt<<1|1);
	pushUp(rt);
}

int query(int L,int R,int l,int r,int rt)
{
	if(L <= l && r <= R)
		return sum[rt];
	int m = (l+r)>>1,ans = 0;
	pushDown(rt,l,r);
	if(L <= m) ans += query(L,R,l,m,rt<<1);
	if(m <  R) ans += query(L,R,m+1,r,rt<<1|1);
	return ans;
}

void updates(int A,int B,int C)
{
	while(top[A] != top[B]){
		if(dep[top[A]] < dep[top[B]]) swap(A,B);
		update(id[top[A]],id[A],C,1,n,1);
		A = f[top[A]];
	}
	if(id[A] > id[B]) swap(A,B);
	update(id[A],id[B],C,1,n,1);
}

int querys(int A,int B)
{
	int ans = 0;
	num = 0; //记录路径上的节点数
	while(top[A] != top[B]){
		if(dep[top[A]] < dep[top[B]]) swap(A,B);
		ans += query(id[top[A]],id[A],1,n,1);
		num += id[A] - id[top[A]] + 1;
		A = f[top[A]];
	}
	if(id[A] > id[B]) swap(A,B);
	num += id[B] - id[A] + 1;
	return ans + query(id[A],id[B],1,n,1);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int m, A;
	string oper;
	cin>>n;
	for(int i = 0; i < n-1; ++i){
		cin>>A;
		add_edge(i+2,A+1);
		add_edge(A+1,i+2);
	}
	cnt = 0;
	dfs1(1,0,1);
	dfs2(1,1);
	build(1,n,1);
	cin>>m;
	while(m--){
		cin>>oper>>A;
		if(oper[0] == 'i'){
			int t = querys(1,A+1);
			cout<<num - t<<'\n';
			updates(1,A+1,1);
		}
		else{
			cout<<query(id[A+1],id[A+1]+size[A+1]-1,1,n,1)<<'\n';
			update(id[A+1],id[A+1]+size[A+1]-1,0,1,n,1);
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值