牛客挑战赛42B 启发式合并

https://ac.nowcoder.com/acm/contest/6944/B

题意

一个有根树模型,根为1,包含点权。
对每个节点u,子树任意LCA(i,j)是u的两点,求最大的GCD(i,j),以及最大情况下ij取法的方案数。

思路

  • 注意到这个题点权的范围和n相同的。
  • 所有数枚举其因子,这些因子放在一个set里,size也不会超过最大数据范围。
  • 统计gcd其实本质就是统计因子。
  • 用一个结构统计子树所有数分解以后因子个数。
  • 计算节点答案就是一些递推累加。
  • 想到启发式合并优化一下,复杂度就差不多了。
  • O ( N ( a i + l o g ( N ) ) ) O(N(\sqrt{a_i} + log(N))) O(N(ai +log(N)))

挺好的启发式合并练手题。

参考代码

vector<int> g[MAXN];
map<ll, ll> p[MAXN];

ll val[MAXN];
ll cnt[MAXN];

void dfs(int u, int f) {
    
    
    for (auto v: g[u]) {
    
    
        if (v == f) continue;
        dfs(v, u);
        if (p[u].size() < p[v].size()) {
    
    
            p[u].swap(p[v]);
        }
        for (auto pi : p[v]) {
    
    
            int x = pi.first;
            if (p[u].count(x) == 0) {
    
    
                p[u].emplace(pi);
                continue;
            }
            if (x > val[u]) {
    
    
                val[u] = x;
                cnt[u] = p[u][x] * p[v][x];
            } else if (x == val[u]) {
    
    
                cnt[u] += p[u][x] * p[v][x];
            }
            
            p[u][x] += p[v][x];
        }
    }
//    debug(u, p[u]);
}

void solve(int kaseId = -1) {
    
    
    int n;
    cin >> n;
    for (int i = 2, u, v; i <= n; ++i) {
    
    
        cin >> u >> v;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }

    for (ll i = 1, ai; i <= n; ++i) {
    
    
        cin >> ai;
        for (ll j = 1; j * j <= ai; ++j) {
    
    
            if (ai % j == 0) {
    
    
                p[i].emplace(j, 1);
                p[i].emplace(ai / j, 1);
            }
        }
    }
    dfs(1, -1);
    for (int i = 1; i <= n; ++i) {
    
    
        cout << val[i] << " " << cnt[i] << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/Tighway/article/details/108446267