题解 CF1101D GCD Counting
Date 2019.8.2
题目大意
给出一棵树,树有点权,求树上的一条链满足:链上所有点的点权的gcd>1,且链上的点数最多(注意一个点也可以构成一条链)
思路
题目要求gcd>1,也就是说这条链上的所有的点的点权都能被一个共同的质因数整除。
又因为a[i]<=2*105,所以我们可以筛出这个范围内的所有质数,同时还能找出每个a[i]所有的质因数。
接下来,我们只要跑一个树形dp,求出答案就好。
下面是关于dp的详情:
-
状态
我们定义f[x][i2]为表示x往下挂出的点权都能被x的第i2个质因数整除的最长链。 -
转移
我们枚举 x的儿子o,x的质因数序号i2,o的质因数序号i3(通过计算可以得出a[i]的质因数数量很少,可以暴力枚举每一个)
显然,如果s[x][i2]=s[o][i3](s数组用来存储质因数),那么就有:
ans=max(ans,f[x][i2]+f[o][i3])ans=max(ans,f[x][i2]+f[o][i3])ans=max(ans,f[x][i2]+f[o][i3])
f[x][i2]=max(f[x][i2],f[o][i3]+1)f[x][i2]=max(f[x][i2],f[o][i3]+1)f[x][i2]=max(f[x][i2],f[o][i3]+1)
要注意的是,这两者的顺序不能颠倒。
因为在更新ans时,f[x][i2]对应的链在x除o以外的另一个儿子下面。但如果先更新f[x][i2]的值,它有可能被更新为f[o][i3]+1。而此时f[x][i2]+f[o][i3]所对应的已经不再是树上的一条链,也就不能更新ans的值。
注意
需要特判几种情况。
1.a[i]全部为1
直接输出0
2.ans最后的值是0,但并不是第一种情况
输出1(一个点也可以构成一条链)
下面附上我的代码
#include<bits/stdc++.h>
#define maxn 200009
using namespace std;
int cnt,Next[maxn*2],head[maxn],to[maxn*2],vis[maxn],sum[maxn];
int f[maxn][50],n,a[maxn],maxx,ans,flag,s[maxn][50],b[maxn];
void Add (int x,int y)
{
cnt++;
to[cnt]=y;
Next[cnt]=head[x];
head[x]=cnt;
}
void Shai ()
{
vis[1]=1;
for (int i=2;i<=sqrt(maxn);i++)
if (!vis[i])
for (int i2=2;i2<=maxn/i;i2++)
vis[i*i2]=1;
}
void Dfs (int x)
{
for (int i=head[x];i;i=Next[i])
{
int o=to[i];
if (b[o])
continue;
b[o]=1;
Dfs(o);
if (sum[x]&&sum[o])
for (int i2=1;i2<=sum[x];i2++)
for (int i3=1;i3<=sum[o];i3++)
{
if (s[x][i2]!=s[o][i3])
continue;
ans=max(ans,f[x][i2]+f[o][i3]);
f[x][i2]=max(f[x][i2],f[o][i3]+1);
}
}
}
int main ()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]!=1)
flag=1;//用于特判
}
for (int i=1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
Add(u,v),Add(v,u);
}
Shai();//筛出所有的质数
for (int i=1;i<=n;i++)
for (int i2=1;i2<=sqrt(a[i]);i2++)//一定不要将循环的上界限设置为a[i],会TLE
{
if (a[i]%i2!=0)
continue;
if (!vis[i2])
{
sum[i]++;
s[i][sum[i]]=i2;
f[i][sum[i]]=1;
}
if (!vis[a[i]/i2]&&i2!=a[i]/i2)//需要注意一下,如果i2*i2==a[i],不要重复记录
{
sum[i]++;
s[i][sum[i]]=a[i]/i2;
f[i][sum[i]]=1;
}
}
b[1]=1;
Dfs(1);
if (ans)
cout<<ans<<endl;
else
cout<<flag<<endl;
return 0;
}
尾记
又调了两个多小时,最后发现居然是邻接表忘乘2了。菜的真实QAQ
博客详细解析了CF1101D GCD Counting问题,通过筛选质因数和树形DP解决求最大点数链的问题。在dp过程中,博主强调了状态定义、转移方程以及特殊情况的处理,如所有点权为1的情况。文章结尾分享了调试过程中的教训。
1138

被折叠的 条评论
为什么被折叠?



