题目描述
给你个节点的一棵树,还有每个点的点权值,现在要你选出一条最长的边,保证这条边中全部的点之后的值不等于,也就是存在一个公共的因子全部点都有。
Solution
思路参考喵渺淼妙的死忠粉大佬这是原题解传送门
观察到这个问题,那么我们第一步想想能不能去枚举,根据唯一分解定理,每一个数都可以拆成一堆质因数幂积的形式。那么现在再看给我们的这棵树,如果把他分解质因数之后,那么可以和它构成一条链的节点一定也存在这些质因数中的一个。那么我们就可以使用一个表,保存全部节点的质因子,我们接下来就可以去枚举这些因子了。那么再枚举这些因子的条件后就变成了求最大的连接的链是多长了,对每个有孩子的子树记录下子树最长的单链,找到最长的两个链连接就行。这个走个即可,关键性的时间复杂度还是前面那个分解全部的数的因子条件,可以看到最极端的情况,个数全部都是大素数里面有个素数,并且都是最接近的那个素数,那么还是会死)大概。那就要加上一个特判大素数的地方,(模板)米勒罗宾素数测试官方题解没加探测函数,然后用我自己的板子发现本来不的加了反而飞了。。就参考了一下YangYL°大佬的板子,把他套在分解质因子前面,好在不炸。
1/14 数据更新了,好像蜜汁卡死了。。等我再看看什么地方死了,待更新。只有87分。
#include <bits/stdc++.h> using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) #define all(__vv__) (__vv__).begin(), (__vv__).end() #define endl "\n" #define pai pair<int, int> #define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__)) #define rep(i, sta, en) for(int i=sta; i<=en; ++i) typedef long long ll; typedef unsigned long long ull; typedef long double ld; inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; } inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op) putchar(op); return; } char F[40]; ll tmp = x > 0 ? x : -x; if (x < 0)putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0)putchar(F[--cnt]); if (op) putchar(op); } inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; } ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; } ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; } const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} }; const int MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; const int N = 32000 + 7; // sqrt(1e9) const int M = 1e5 + 7; vector<int> prime; int cnt, n, m; bool vis[M]; void getprime() { // 欧拉筛法求素数表O(n) memset(vis, true, sizeof(vis)); cnt = 0; for (int i = 2; i < N; ++i) { if (vis[i]) prime.push_back(i), ++cnt; for (int j = 0; j < cnt; ++j) { if (i * prime[j] >= N) break; vis[i * prime[j]] = false; if (i % prime[j] == 0) break; } } } /*米勒罗宾素数测试*/ ll A[11] = { 0,2,3,5,7,11,13,17,19,23,61 }; int Miller_Rabin(int x) {//素数探测算法 int s = 0; if (x == 2) return 1; if (x % 2 == 0 || x == 1) return 0; int p = x - 1; while (p % 2 == 0) p /= 2, s++; for (int j = 1; j <= 5; j++) { long long B = qpow(A[j], p, x); for (int i = 1; i <= s; i++) { long long K = (B * B) % x; if (K == 1ll && B != 1ll && B != x - 1) return 0;//如果二次探测发现这个不是质数 B = K; } if (B != 1ll) return 0; } return 1;//探测正常结束 } int head[M], tot = 0;//前向星变量 struct Node { //int u; //起点 //int w; //权值 int v, next; } edge[M << 1]; void add(int u, int v) { tot++; //edge[tot].u = u; edge[tot].v = v; //edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot; } unordered_map<int, vector<int>> has; int w[M], ans = 1; int dfs(int u, int num) { if (w[u] % num != 0) return 0; vis[u] = 1; int res = 1; int max1 = -1, max2 = -1; for (int i = head[u]; i; i = edge[i].next) { int v = edge[i].v; if (!vis[v]) { int tmp = dfs(v, num); res = max(res, 1 + tmp); if (tmp >= max1) max2 = max1, max1 = tmp; else if (tmp >= max2) max2 = tmp; } } if (max1 != -1 and max2 != -1) ans = max(ans, max1 + max2 + 1); else if (max1 != -1) ans = max(ans, max1); return res; } void solve() { getprime(); n = read(); rep(i, 1, n - 1) { int u = read(), v = read(); add(u, v); add(v, u); } for (int i = 1; i <= n; ++i) { w[i] = read(); int tmp = w[i]; if (Miller_Rabin(tmp)) { prime.push_back(tmp), has[tmp].push_back(i), ++cnt; // 大素数 continue; } for (int j = 0; j < cnt; ++j) { if (tmp == 1) break; if (tmp % prime[j] == 0) { has[prime[j]].push_back(i); while (tmp % prime[j] == 0) tmp /= prime[j]; } } if (tmp != 1) prime.push_back(tmp), has[tmp].push_back(i), ++cnt; } for (auto& it : prime) { ms(vis, 0); for (auto& pos : has[it]) if (!vis[pos]) dfs(pos, it); } print(ans); } int main() { //int T = read(); while (T--) solve(); return 0; }