P2441 角色属性树 题解

题目

我们发现,如果两个数 x x x y y y 满足 gcd ⁡ ( x , y ) ≠ 1 \gcd(x,y) \not=1 gcd(x,y)=1,那么这两个数就有同一个属性。

证明:设 gcd ⁡ ( x , y ) \gcd(x,y) gcd(x,y) k k k,那么如果 k k k 是一个质数,那么 x , y x,y x,y 就有一个为 k k k 的属性,否则,将 k k k 分解质因数,也可以得到一个可以同时被 x , y x,y x,y 整除的质数。

我们看向数据范围: n ≤ 2 ⋅ 1 0 5 n \le 2 \cdot 10^5 n2105,但是有一句话给我们指明了方向:

本题测试数据随机,可能是假题。

所以随机数据造出来的树大概率不是这种树:

退化成链的树

如果真的有这种退化成链的树,那么出题人可以立马去买一张彩票。

我们算一下这种树出现的概率:如果长度只有 10000 10000 10000,因为这种长度就足够让我们超时了:

首先,每一个节点都有一个固定的父亲,这里,有 10000 10000 10000 个节点都只有一个儿子:

先假设只有 1000 1000 1000 个节点,第一个节点先选一个父亲, 9999 9999 9999 个(排除开自己),然后第二个节点再在剩下的 9998 9998 9998 个节点中再选一个父亲:不重复的概率为 9998 9999 \frac{9998}{9999} 99999998,然后每一个节点都选一个父亲:总的概率为 ! 9999 999 9 10000 \frac{!9999}{9999^{10000}} 999910000!9999,一共有 2 ⋅ 1 0 5 2\cdot 10^5 2105 个节点,在其中选 10000 10000 10000 个,不考虑顺序,再算一下概率: ! 9999 999 9 10000 ⋅ C 200000 10000 \frac{!9999}{9999^{10000}}\cdot C_{200000}^{10000} 999910000!9999C20000010000

估算一下,左边估一下: 10000 × 500 0 2 × 250 0 4 × 125 0 8 ⋯ 1 10000 10000 \times 5000 ^ 2 \times 2500 ^ 4 \times 1250 ^ 8 \cdots 1 ^ {10000} 10000×50002×25004×12508110000,大于等于 1 0 35 10^{35} 1035,右边约为 1 0 20 10^{20} 1020,所以大约为 1 1 0 15 \frac{1}{10^{15}} 10151

所以暴力就可以了。

往上找,找到满足条件后就可以了,由于大部分可以在 5 5 5 次内找到,超过 log ⁡ 2 ( n ) \log_2(n) log2(n) 才能找到的概率很小。

AC Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
int n, k;
int a[200100];
int f[200100];

int root;
int op, x, y;
int gcd(int x, int y) {return y == 0 ? x : gcd(y, x % y);}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> k;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		f[v] = u;
	}
	for (int i = 1; i <= n; i++) if (!f[i]) root = i;
	while (k--) {
		op = 0;
		cin >> op >> x;
		if (op == 1) {
			int st = x;
			x = f[x];
			while (gcd(a[st], a[x]) == 1) {
			 	if (x == 0) break;
				x = f[x];
			}
			cout << (x == 0 ? -1 : x) << '\n';
		}
		else {
			cin >> y;
			a[x] = y;
		}
	}
	return 0;
}
  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值