牛客挑战赛42 小睿睿的伤害

树上启发式合并

枚举每个数字的所有因子,存起来

官方题解传送门   https://ac.nowcoder.com/discuss/485120?type=101&order=0&pos=2&page=1&channel=-1&source_id=1

题目传送门   https://ac.nowcoder.com/acm/contest/6944/B

具体看代码吧,有注释

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
const int maxn = 1e5 + 100;

typedef long long ll;
vector<int>G[maxn];
void add(int be, int en) {
	G[be].push_back(en);
}
int siz[maxn];
int son[maxn];

int dfs(int x, int fa) {
	siz[x] = 1;
	int c = 0;
	for (int p : G[x]) {
		if (p == fa) continue;
		dfs(p, x);
		siz[x] += siz[p];
		if (siz[p] > c) {
			c = siz[p];
			son[x] = p;
		}
	}
	return 0;
}
ll ans[maxn];//最大约数
ll num[maxn];//有num对

ll cnt[maxn];//公共资源,p这个数字是cnt【p】个数字的因子

int list[maxn];
vector<int>ins[maxn];


int cal(int x, int fa, int f) {//计算cnt,f是1就是加,f=-1就是清除答案
	for (int p : ins[list[x]]) cnt[p] += f;

	for (int p : G[x]) {
		if (p == fa) continue;
		cal(p, x, f);
	}
	return 0;
}

int cal_ans(int x, int fa,int f,ll &cn,ll &an) {//重儿子算过了,只算轻儿子,cn和an就是cnt[x]和ans[x]

	for (int p : ins[list[x]]) {
		if (cnt[p]) {
			if (p > an) {
				an = p;
				cn = cnt[p];
			}
			else if(p == an){
				cn += cnt[p];
			}
		}
	}

	for (int p : G[x]) {
		if (p == fa || p == f) continue;
		cal_ans(p, x, f, cn, an);
	}
	return 0;
}

int dfs2(int x, int fa, int flag) {
	for (int p : G[x]) {
		if (p == fa) continue;
		if (p == son[x]) continue;
		dfs2(p, x, 0);
	}

	if (son[x] != 0) {
		dfs2(son[x], x, 1);
	}

	//num[x] = ans[x] = 0;

	for (int p : G[x]) {
		if (p == fa || p == son[x]) continue;
		cal_ans(p, x, son[x], num[x], ans[x]);
		cal(p, x, 1);
	}
	
	for (int p : ins[list[x]]) {
		if (cnt[p]) {
			if (p > ans[x]) {
				ans[x] = p;
				num[x] = cnt[p];
			}
			else if(p == ans[x]){
				num[x] += cnt[p];
			}
		}
		cnt[p]++;
	}

	if (flag == 0) {
		cal(x, fa, -1);
	}
	return 0;
}



int main() {
	for (int i = 1; i < maxn; i++) {
		for (int j = 1; j*i < maxn; j++) {
			ins[i*j].push_back(j);//i*j的所有因子由j组成
		}
	}

	int n;
	int x, y;
	scanf("%d", &n);
	for (int i = 2; i <= n; i++) {
		scanf("%d %d", &x, &y);
		add(x, y);
		add(y, x);
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &list[i]);
	}

	dfs(1, -1);//找重儿子

	dfs2(1, -1, 0);//启发式合并


	for (int i = 1; i <= n; i++) {
		printf("%lld %lld
", ans[i], num[i]);
	}
	return 0;
}

  

寻找真正的热爱
原文地址:https://www.cnblogs.com/lesning/p/13557091.html