题解 CF1101D GCD Counting

题解 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数组用来存储质因数),那么就有:
    a n s = m a x ( a n s , f [ x ] [ i 2 ] + f [ o ] [ i 3 ] ) ans=max(ans,f[x][i2]+f[o][i3]) ans=max(ans,f[x][i2]+f[o][i3])
    f [ x ] [ i 2 ] = m a x ( f [ x ] [ i 2 ] , f [ o ] [ i 3 ] + 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
#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<

尾记

又调了两个多小时,最后发现居然是邻接表忘乘2了。菜的真实QAQ

你可能感兴趣的:(题解)