数据结构_叶子节点的增删

EOJ Monthly 2021.1 Sponsored by TuSimple

C. 魔树

题意:

第 0年,Cuber QQ 种下了一棵魔树。这是一棵有 n个结点的有根树,它的根为 1 号结点。

接下来 m 年,每年 Cuber QQ 都会施展魔法来改变这棵树。有两种魔法:

A k:对于树中的每个叶子,让它长出 k 个并排的孩子;
D: 对于树中的所有叶子,将它们删除。

有根树的叶子是指没有孩子的点。注意对于操作完的树,它的叶子集合可能会改变。

现在你知道了 Cuber QQ 每年施展的魔法。你想要预测 m 年以后,魔树会包含多少个点。你只需要求出答案对1e9 + 7 取模的结果。

保证 Cuber QQ 不会在根结点为叶子时使用第二种魔法。

输入格式
第一行两个整数 n,m(1<=n,m<=1e5)。

接下来 n - 1 行,每行两个整数 u,v(1 <= u,v <= n,u≠v),表示第 0年魔树中的一条边。

接下来 m行,第 i 行一个形如 A k(1 <= k <= 1e+7)或 D 的字符串,表示 Cuber QQ 第i 年进行的操作。

输出格式
一行一个整数,表示答案。

样例
input
5 3
1 2
2 3
2 4
4 5
A 1
D
A 2
output
9
input
1 10
A 2
D
A 3
A 4
D
A 5
A 6
A 7
D
A 8
output
829
提示
对于样例一,第 1年加入的点在第 2年全被删除,第 3年加入了 4 个点,故最终有 9个点。

思路:

首先我们用一个栈维护增删操作.
如果操作是’A’,就加入栈
如果是D,栈空 cnt++(cnt记录一共要先删除几层)
栈不为空,弹出栈顶.

其次我们怎么删除呢???
我们先记录最开始的那棵树每个点距离叶子结点的最远距离(因为可能不止一个叶子,需要比较,取最大值),把距离小于cnt的删掉就可以

怎么增加呢???
假设我第一次k = 3
第二次k = 4
那么就增加了3 * 4 = 12 个结点,以此类推乘法

代码实现:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const ll mod = 1e9 + 7;
vector<int>G[maxn];
stack<char> st;
stack<int> s;
int dep[maxn];
int vis[maxn];
int dfs(int now){
	int len = G[now].size();
	if(len == 0){
		dep[now] = 0;
	}
	for(int i = 0;i < len;i++){
		int cur = G[now][i];
		if(!vis[cur]){
			vis[cur] = 1;
			dep[now] = max(dfs(cur) + 1,dep[now]);
		}
	}
	return dep[now];
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	int u,v;
	for(int i = 1;i < n;i++){
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	memset(dep,0,sizeof(dep));
	memset(vis,0,sizeof(vis));
	vis[1] = 1;
	dep[1] = dfs(1);
	char x;
	int y;
	getchar();
	int f = 0;
	for(int i = 1;i <= m;i++){
		scanf("%c",&x);
		getchar();
		if(x == 'A'){
			scanf("%d",&y);
			getchar();
			st.push(x);
			s.push(y);
		}
		else{
			if(!st.empty()){
				st.pop();
				s.pop();
			}
			else{
				f++;
			}
		}
	}
	int pre[maxn];
	memset(pre,0,sizeof(pre));
	int tot = 0;
	while(!s.empty()){
		pre[++tot] = s.top();
		s.pop();
	}

	ll ans = 0;
	ll res = 0;
	for(int i = 1;i <= n;i++){
		//假设f = 0,不会删除任何结点。
		if(dep[i] > f){
			ans++;
		}
		if(dep[i] == f){
			res++;
		}
	}
	//先加再取模和先取模再加得到的结果是不一定相同的。
	for(int i = tot;i >= 1;i--){
		ans = (ans + res) % mod;
		res = (res * pre[i]) % mod;
	}
	ans = (ans + res) % mod;
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值