[CF1101D]GCD Counting

做题时间:2022.7.6

\(【题目描述】\)

给一棵 \(N(N\leq 2\times 10^5)\) 个节点的树,每一个节点有一个点权 \(w_i(1\leq w_i\leq 2\times 10^5)\) ,求最长的所有点权最大公约数大于1的链。

\(【输入格式】\)

第一行一个整数 \(N\)
第二行 \(N\) 个整数表示 \(w_i\)
接下来 \(N-1\) 行每行两个整数 \(u,v\) 表示这两个点之间有一条边

\(【输出格式】\)

一行一个整数表示答案

\(【考点】\)

质因数分解,树形DP

\(【做法】\)

处理链 \((u,v)\) 的问题一般是拆成 \((u,lca_{u,v})\)\((v,lca_{u,v})\) 解决 ,因此每一个点只需要考虑其子节点的情况,可以使用树形DP解决。

由于最大公约数只与点权的所有质因子有关,且点权最大只有 \(2\times 10^5\) ,拆成质因子之后其个数仅有不到 \(10\) 个,同时其对答案有影响废话 ,可以考虑将每个数质因数分解,并将分解后的质因子作为DP的一维。

\(f_{u,i}\) 表示点 \(u\)\(i\) 个质因子作为 \(u\)为起点的 链上所有点权的公约数时链的最大长度(注意这里不是最大公约数),很容易就可以推出方程:

\[f_{u,i}=\max\limits_{v\in son_u,fac_{u,i}=fac_{v,j}}(f_{v,j})+1 \]

这里的 \(fac_{i,j}\) 表示点 \(i\) 的第 \(j\) 个质因子。

然后,回到一开始时的拆链操作,现在合并,将同一个点的 \(f\) 合并在一起。定义 \(g_{u,i}\) 表示以 \(u\) 为顶点、所有点权的因数为 \(i\) 的最长链,也就是答案,有:

\[g_{u,i}=\max\limits_{v,w\in son_u,fac_{v,j}=fac_{w,k}=fac_{u,i}}(f_v,j+f_w,k) \]

看着很复杂,但实际上和第一个式子有很多相似之处,可以同时转移。具体可以见代码。

#include<cstdio>
#include<iomanip>

using namespace std;
const int N=2e5+50,M=34;
struct edge{
	int to,nxt;
}a[N<<1];
int head[N],f[N][M],g[N][M],cnt,n,ans;
int Fac[N][M],Cnt[N],w[N];
inline int Max(int a,int b){return a>b?a:b;}
void Div(int x,int pos)//质因数分解 
{
	for(int i=2;i*i<=x;i++){
		if(x%i==0) Fac[pos][++Cnt[pos]]=i;
		while(x%i==0) x/=i;
	}
	if(x>1) Fac[pos][++Cnt[pos]]=x;
}
void add(int u,int v)
{
	cnt++;
	a[cnt].to=v;
	a[cnt].nxt=head[u];
	head[u]=cnt;
}
void DFS(int u,int fa)
{
	for(int k=head[u];k;k=a[k].nxt){
		int v=a[k].to;
		if(v!=fa){
			DFS(v,u);
			for(int i=1;i<=Cnt[u];i++){
				for(int j=1;j<=Cnt[v];j++){
					if(Fac[u][i]==Fac[v][j]){
						g[u][i]=Max(g[u][i],f[u][i]+f[v][j]);
						//这个地方,是在转移当前的 $f_{u,i}$ 之前。
						//此时 $f_{u,i}$ 已经代表了此前访问过的所有 u 的儿子中符合要求且最长的链
						//只需要与当前所转移的 f[v][j]求和即可 
					
						f[u][i]=Max(f[u][i],f[v][j]+1);
					}
				}
			}
		}
	}
	
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&w[i]);
		Div(w[i],i);
	}
	int u,v;
	for(int i=1;i<=n-1;i++){
		scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=Cnt[i];j++) f[i][j]=g[i][j]=1;
	}//一开始每一个点都是一条链 
	DFS(1,0);
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=Cnt[i];j++) ans=Max(ans,g[i][j]);
		//这里注意每一个点都可以作为链的顶点 
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-07-06 20:31  lxzy  阅读(24)  评论(0)    收藏  举报