cogs2783alone

本文介绍了一种利用记忆化线段树解决特定问题的方法,该问题涉及对一棵树状结构进行更新和查询操作,具体包括好感度下降操作及查询好感度大于0的节点数量。文章提供了完整的代码实现,并解释了如何通过记忆化避免重复计算,从而优化时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目描述】

HA的MK 以前曾经同时与一些傻二哈交♂往♂,傻二哈们之间的关系成一棵树。

一开始每个傻二哈对他都有一个好感度。因为 MK 太神犇了,所以很多傻二哈给他出了一些题目,

他每 AC 一道 编号为WCG的傻二哈 出给他的题目会导致以 WCG为根的子树中除了 WCG 以外所有的傻二哈对他的好♂感♂度

下降。

操作 1: MK AC 了 WCG 出的题目导致以 WCG为根的子树中除了 WCG 以外所有的傻二哈对他的好♂感♂度下

降。

操作 2: MK 想要知道以 WCG 为根的子树中除了 WCG 以外还有几个对他好感度>0 的。

树根处的傻二哈编号为 0,它对 MK 的好♂感♂度♂可以看做是无限的

【输入格式】

第一行一个整数 N。

代表有 N+1 个傻二哈,编号分别是 0~N。

接下来 N 行每行两个整数,第一个整数表示编号为 i 的傻二哈的好♂感♂度♂ H,第二个整数表示第

i 个傻二哈在树上的父亲 Fi。(保证傻二哈 i 的父亲的编号小于 i)。

接下来一行一个整数 Q。

接下来 Q 行,每行一个操作。

第一类操作读入三个参数{1,Ai,Xi}表示 QY 使以 Ai 为根的子树中除了 Ai 以外所有的傻二哈

对他的好感度下降 Xi。

第二类操作读入两个参数{2,Ai}表示询问以 Ai 为根的子树中除了 Ai 以外有几个傻二哈对 MK

的好感度>0。

【输出格式】

对于每一个第二类操作,输出一行一个整数,表示所询问的答案。

【样例输入】

4
1 0
2 0
2 2
1 2
4
1 2 1
2 2
1 0 1
2 0

【样例输出】

1
1

【提示】

对于 30%的数据,满足 1<=N<=1000,1<=Q<=1000。

对于另外 20%的数据,保证数据纯随机生成。

对于 100%的数据,满足 1<=N<=10^5,1<=Q<=10^5,0<=Ai<=N,1<=Hi<=10^9,1<=Xi<=10^4,

0<=Fi<i。

【来源】

在此键入。

题解:记忆化线段树 删点的时候直接走到叶子并记录是否走过 走过了就不再走一遍 时间复杂度O(nlogn)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=100000+10;
vector<int>A[maxn];
int B[maxn];
int dfn[maxn];
int size[maxn];
int v[maxn];
int ti=0;
int minn[maxn*4],num[maxn*4],tag[maxn*4];
int ql,qr;
inline void dfs(int x,int f){
	dfn[x]=++ti;
	v[ti]=B[x];
	size[x]=1;
	for(int i=0;i<A[x].size();i++){
		int u=A[x][i];
		if(u==f)
			continue;
		dfs(u,x);
		size[x]+=size[u];
	}
}
inline void pushdown(int o){
	minn[o<<1]-=tag[o];
	minn[o<<1|1]-=tag[o];
	tag[o<<1]+=tag[o];
	tag[o<<1|1]+=tag[o];
	tag[o]=0;
}
inline void build(int o,int l,int r){
	if(l==r){
		num[o]=(v[l]>0);
		minn[o]=v[l];
	}
	else {
		int mid=(l+r)>>1;
		build(o<<1,l,mid);
		build(o<<1|1,mid+1,r);
		minn[o]=min(minn[o<<1],minn[o<<1|1]);
		num[o]=num[o<<1]+num[o<<1|1];
	}
}
inline void add(int o,int l,int r,int x){
	if(ql<=l&&r<=qr){
		tag[o]+=x;
		minn[o]-=x;
		//pushdown(o);
		if(minn[o]<=0){
			if(l==r){
				num[o]=0;
				minn[o]=0x7fffffff;
				tag[o]=0;
				return ;
			}
			else {
				pushdown(o);
				int mid=(l+r)>>1;
				add(o<<1,l,mid,0);
				add(o<<1|1,mid+1,r,0);
				minn[o]=min(minn[o<<1],minn[o<<1|1]);
				num[o]=num[o<<1]+num[o<<1|1];
			}
		}
	}
	else if(l!=r){
		pushdown(o);
		int mid=(l+r)>>1;
		if(ql<=mid)
			add(o<<1,l,mid,x);
		if(qr>mid)
			add(o<<1|1,mid+1,r,x);
		minn[o]=min(minn[o<<1],minn[o<<1|1]);
		num[o]=num[o<<1]+num[o<<1|1];
	}
}
inline int query(int o,int l,int r){
	if(ql<=l&&r<=qr)
		return num[o];
	else if(l!=r){
		int mid=(l+r)>>1;
		int ans=0;
		if(ql<=mid)
			ans+=query(o<<1,l,mid);
		if(qr>mid)
			ans+=query(o<<1|1,mid+1,r);
		return ans;
	}
	return 0;
}
int main(){
	freopen("alone.in","r",stdin);
	freopen("alone.out","w",stdout);
	int n,m;
	scanf("%d",&n);
	n++;
	int x;
	for(int i=2;i<=n;i++){
		scanf("%d %d",&B[i],&x);
		A[x+1].push_back(i);
	}
	dfs(1,0);
	build(1,1,n);
	scanf("%d",&m);
	int p;
	for(int i=1;i<=m;i++){
		scanf("%d",&p);
		if(p==1){
			int k;
			scanf("%d %d",&x,&k);
			x++;
			ql=dfn[x]+1;
			qr=dfn[x]+size[x]-1;
			if(ql<=qr)
				add(1,1,n,k);
		}
		else {
			scanf("%d",&x);
			x++;
			int ans=0;
			ql=dfn[x]+1;
			qr=dfn[x]+size[x]-1;
			if(ql<=qr)
				ans=query(1,1,n);
			printf("%d\n",ans);
		}
	}
return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值