设ans[i]表示长度为i的链gcd最大是多少,由于长度减小时gcd的最大值是不降的,显然ans[i]=max(ans[i],ans[i+1]);
那么考虑怎么求出ans[i]。
从小到大枚举gcd,将所有权值是gcd倍数的边都加到树,然后树形dp求最长链。
这样的话,每一个数的因数都是 n√ 级别的,所以每一条边都只会被加根n次,时间复杂度 O(n√) 。
#include
#include
#include
using namespace std;
#define N 1000005
int n,w,top,id,now,Max;
int ans[N],stack[N],vis[N],f[N],g[N];
struct hp{int x,y;}edge[N];
int tot,point[N],nxt[N*2],v[N*2];
int totw,pointw[N],nxtw[N*2],vw[N*2];
void _addedge(int x,int y)
{
++totw; nxtw[totw]=pointw[x]; pointw[x]=totw; vw[totw]=y;
}
void addedge(int i)
{
++tot; nxt[tot]=point[edge[i].x]; point[edge[i].x]=tot; v[tot]=edge[i].y;
++tot; nxt[tot]=point[edge[i].y]; point[edge[i].y]=tot; v[tot]=edge[i].x;
stack[++top]=edge[i].x,stack[++top]=edge[i].y;
}
void dfs(int x,int fa)
{
vis[x]=id;f[x]=g[x]=0;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
dfs(v[i],x);
if (f[v[i]]+1>f[x])
{
g[x]=f[x];
f[x]=f[v[i]]+1;
}
else g[x]=max(g[x],f[v[i]]+1);
}
now=max(now,f[x]+g[x]);
}
int main()
{
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
scanf("%d",&n);
for (int i=1;iscanf("%d%d%d",&edge[i].x,&edge[i].y,&w);
_addedge(w,i);Max=max(Max,w);
}
for (int i=1;i<=Max;++i)
{
top=0;++id;now=0;tot=0;
for (int j=i;j<=Max;j+=i)
for (int k=pointw[j];k;k=nxtw[k])
addedge(vw[k]);
for (int k=1;k<=top;++k)
if (vis[stack[k]]!=id) dfs(stack[k],1);
for (int k=1;k<=top;++k)
point[stack[k]]=0;
ans[now]=i;
}
for (int i=n;i>=1;--i)
ans[i]=max(ans[i],ans[i+1]);
for (int i=1;i<=n;++i) printf("%d\n",ans[i]);
}