GCD Counting

提供一个非常容易的做法。

还是考虑点分治,但不用莫反,也不用容斥。

考虑找出重心后深搜,找到每个点到重心的路径的点权 gcd\gcd。把他们全都扔到 set 里面,直接枚举 set 里面两个数就好。

这看起来很暴力,其实是个经典套路。由于 gcd\gcdO(logv)O(\log v) 的变化次数内就会成为 11,于是暴力枚举的复杂度是 O(log2v)O(\log^2 v) 左右的,而且不太能跑满。

由于限时比较大,所以没有什么压力,复杂度应该是大约是三只 log\log 的?

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <numeric>
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;

const int N = 2e5 + 5;

int n, a[N];
vector<int> G[N];
bool del[N];
unordered_map<int, int> cnt, tcnt;
unordered_set<int> st, tst;
int wc, tot;
int sz[N];
long long ans[N];
void dfs_sz(int u, int fa)
{
	sz[u] = 0;
	if (del[u]) return;
	sz[u] = 1;
	for (auto& j : G[u])
	{
		if (j != fa)
		{
			dfs_sz(j, u);
			sz[u] += sz[j];
		}
	}
}

void dfs_wc(int u, int fa)
{
	if (del[u]) return;
	int maxn = tot - sz[u];
	for (auto& j : G[u])
	{
		if (j != fa)
		{
			dfs_wc(j, u);
			maxn = max(maxn, sz[j]);
		}
	}
	if (maxn <= tot / 2) wc = u;
}

int dis[N];

void dfs_dis(int u, int d, int fa)
{
	if (del[u]) return;
	dis[u] = gcd(d, a[u]);
	ans[dis[u]]++;
	cnt[dis[u]]++;
	tcnt[dis[u]]++;
	tst.insert(dis[u]);
	st.insert(dis[u]);
	for (auto& j : G[u])
	{
		if (j != fa) dfs_dis(j, dis[u], u);
	}
}

void calc(int u)
{
	if (del[u]) return;
	cnt.clear();
	st.clear();
	wc = 0;
	tot = 0;
	dfs_sz(u, 0);
	tot = sz[u];
	dfs_wc(u, 0);
	u = wc;
	del[u] = 1;
	tcnt.clear();
	tst.clear();
	for (auto& j : G[u])
	{
		cnt.clear();
		st.clear();
		dfs_dis(j, a[u], u);
		for (auto& k1 : st)
		{
			for (auto& k2 : st)
			{
				if (k2 < k1) continue;
				if (k1 == k2)
				{
					ans[k1] -= (1LL * cnt[k1] * (cnt[k1] - 1LL) / 2LL);
				}
				else
				{
					ans[gcd(k1, k2)] -= (1LL * cnt[k1] * cnt[k2]);
				}
			}
		}
	}
	for (auto& k1 : tst)
	{
		for (auto& k2 : tst)
		{
			if (k2 < k1) continue;
			if (k1 == k2)
			{
				ans[k1] += (1LL * tcnt[k1] * (tcnt[k1] - 1LL) / 2);
			}
			else
			{
				ans[gcd(k1, k2)] += (1LL * tcnt[k1] * tcnt[k2]);
			}
		}
	}
	//cout << "\n\n";
	for (auto& j : G[u])
	{
		calc(j);
	}
	
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);
		ans[a[i]]++;
	}
	for (int i = 1; i < n; i++)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		G[u].emplace_back(v);
		G[v].emplace_back(u);
	}
	calc(1);
	for (int i = 1; i <= 200000; i++)
	{
		if (ans[i])
		{
			printf("%d %lld\n", i, ans[i]);
		}
	}
	return 0;
}
posted @   HappyBobb  阅读(18)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示