打卡信奥刷题(3544)用C++实现信奥题 P11141 [APC001] F - Extend

P11141 [APC001] F - Extend

题目背景

本题输入输出量较大,请酌情使用较快的输入输出方式。已扩大时限从 1s→1.5s1s\to 1.5s1s1.5s

题目描述

对于一棵有 nnn 个节点,根为 kkk 的树,最开始有且仅有一个“可扩展的”节点 zzz,我们有两种扩展形式:

  • I\text{I}I 类扩展:从一个“可扩展的”节点 uuu 开始扩展,把 uuu 的子树中所有节点和所有满足与 uuu 距离 ≤p\leq pp 且是 uuu 祖先的节点标记为“可扩展的”。
  • II\text{II}II 类扩展:从一个“可扩展的”节点 uuu 开始扩展,把所有与 uuu 深度相等的节点标记为“可扩展的”。

你需要将所有结点都标记为“可扩展的”,求最小进行扩展的次数。

输入格式

第一行两个整数 n,kn,kn,k

接下来 n−1n-1n1 行,每行两个整数 u,vu,vu,v,表示存在一条无向边连接 u,vu,vu,v

接下来一行一个整数 ttt,表示询问次数。

接下来 ttt 行,每行两个整数 z,pz,pz,p,意义如上。

输出格式

ttt 行,对于每次询问:

  • 若无法将所有节点都标记为“可扩展的”,输出 -1

  • 否则一行一个整数,表示答案。

输入输出样例 #1

输入 #1

3 1
1 2
1 3
2
2 6
2 1

输出 #1

2
2

输入输出样例 #2

输入 #2

4 1
2 1
3 2
4 3
3
2 2
2 5
4 2

输出 #2

1
1
2

输入输出样例 #3

输入 #3

20 11
2 8
16 4
6 7
11 8
10 13
7 10
17 7
15 13
10 14
18 19
1 2
6 3
12 1
1 5
1 4
2 3
9 5
14 20
5 18
15
9 269
1 522
6 327
13 726
14 8
17 2
18 4
15 64
9 5
13 3
18 1
19 664
5 3
20 5
6 4

输出 #3

2
2
2
2
2
3
2
2
2
3
5
2
2
3
2

说明/提示

样例 111 解释:两次询问都可以先对 222 节点进行 II\text{II}II 类扩展,然后对 333 进行 I\text{I}I 类扩展。可以保证对于两次询问,这样操作都是最优的。

对于所有数据,1≤z,k,u,v≤n≤105,1≤t≤2×106,0≤p≤10181\leq z,k,u,v\leq n\leq 10^5,1\leq t\leq 2\times 10^6,0\leq p\leq 10^{18}1z,k,u,vn105,1t2×106,0p1018

C++实现

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int n, k, cnt, dep[maxn];
vector<int> T[maxn];
void dfs(int u, int fa){ // 统计深度
	dep[u] = dep[fa] + 1;
	for(int v : T[u]){
		if(v == fa) continue;
		dfs(v, u);
	}
}
void dfs2(int u, int fa){ // 统计(说过的特殊情况)从根节点到首次分岔的这条链的长度
	int child = 0, nxt = -1;
	for(int v : T[u]){
		if(v == fa) continue;
		child ++; nxt = v;
	}
	if(child > 1 || nxt == -1) return ; // 到分岔了 或 到叶子了,返回
	cnt ++;
	dfs2(nxt, u);
}
void solve(){
	cin >> n >> k;
	for(int i = 1; i <= n - 1; i ++){
		int u, v; cin >> u >> v;
		T[u].push_back(v), T[v].push_back(u);
	}
	dep[0] = -1; // 根节点层为0,这样计算起来会方便一些
	dfs(k, 0);
	dfs2(k, 0);
	int T; cin >> T;
	while(T --){
		int z, p; cin >> z >> p;
		if(n == 1) cout << "0\n";
		else if(p == 0) cout << "-1\n";
		else if(z == k) cout << "1\n";
		else{
			int t = (dep[z] % p == 0? p : dep[z] % p); // 找倒数第二次时距离根结点的距离,注意特判整除
			if(t > cnt) cout << (dep[z] + p - 1) / p + 1 << '\n'; // ceil(dep[z]/p)+1
			else cout << (dep[z] + p - 1) / p << '\n'; // 否则能省一步
		}
	}
}

signed main(){
	solve();
	return 0;
}

在这里插入图片描述

后续

接下来我会不断用C++来实现信奥比赛中的算法题、GESP考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值