CF1101D GCD Counting

题目传送门:CF1101D

洛谷入口

题目大意:

\(给出一棵n个节点的树,每个节点上有点权a_i\)
\(求最长的树上路径,满足条件:\)
\(路径上经过节点(包括两个端点)点权的gcd和不等于1\)

数据范围

\(\circ\) \(n\le2\times10^5\)
\(\circ\) \(1\le a_i\le2\times10^5\)

题解

首先当然会想到
若要几个的gcd不等于0
我们可以用他们的其中一个非1的因数DP
向下DP显然不妙,于是决定DFS树,回溯时去DP
那么对于一个点,遍历他的儿子
对于每一个子节点回溯时找他们两个的所有相同质因子
进行DP,同时更新答案
最后输出答案即可
对于找共同质因子,只要预先处理:
先找到质数家族,
再一个一个\(a_i\)分解质因数,把它们放在数组里储存。
怕数组空间炸?不用担心,一个数最多也就\(7,8\)个不同的质因子
\((2\times3\times5\times7\times9\times11\times13\times17>2\times10^5)\)
所以就问题不大了!详见下方代码↓↓↓

AC代码:

#include<bits/stdc++.h>
using namespace std;
int tot=0,n,cnt,head[200010];
int maxx=200010,a[200010],vis[300010],f[200010][10],y[200010][10],ans,p[200010];
//p是质数家族 ,y[i][j]是指a[i]的第j大的质因子 
struct abc{
	int to,nxt;
}e[800010];
void add(int u,int v){
	e[++cnt].nxt=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}
void make_zhi(){//求质数的欧拉筛,不懂自行百度(相信各位大佬定会的) 
	for(int i=2;i<=maxx;i++){
		if(!vis[i])p[++tot]=i;
		for(int j=1;j<=tot&&p[j]*i<=maxx;j++){
			vis[i*p[j]]=1;
			if(i%p[j]==0)break;
		}
	}
	//cout<<p[10]<<endl;
}
void dfs(int u,int fa){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(fa==v)continue;
		dfs(v,u);
		for(int j=1;j<=y[u][0];j++){
			for(int k=1;k<=y[v][0];k++){
				if(y[u][j]==y[v][k]){
					ans=max(ans,f[u][j]+f[v][k]+1);
					f[u][j]=max(f[u][j],f[v][k]+1);
				}
			}
		}
	}
}
int main(){
	make_zhi();
	int ff=1;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]!=1)ff=0;
	}
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);add(v,u);
	}
	if(ff){//特判全1时 
		cout<<0;
		return 0;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;p[j]*p[j]<=a[i];j++){
			if(a[i]==1)break;
			if(a[i]%p[j]==0){
				y[i][++y[i][0]]=p[j];//太懒于是用y[i][0]存质数个数 
				while(a[i]%p[j]==0)a[i]/=p[j];//统统除掉 
			}
		}
		if(a[i]!=1)y[i][++y[i][0]]=a[i];//剩下了他自己怎么办?靠他了 
	}
	dfs(1,0);
	cout<<ans+1;
} 
//加一什么的多调几次就会发现的了 

支持一下吧,关注,点赞,评论都好!

THE END

posted @ 2020-03-29 22:27  Realityang  阅读(201)  评论(0编辑  收藏  举报