[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;
}

浙公网安备 33010602011771号