教练某场胡策的$T2$。

题目

描述

给定一棵$n$个节点的树,每条边的长度为$1$,同时有一个权值
$w$。定义一条路径的权值为路径上所有边的权值的最大公约数。现在
对于任意$i\in[1,n]$,求树上所有长度为$i$的简单路径中权值最大的
是多少。如果不存在长度为$i$的路径,则第$i$行输出$0$。

输入输出格式

输入格式

第一行,一个整数$n$,表示树的大小。
接下来$n-1$行,每行三个整数$u,v,w$,表示$u,v$间存在一条权值
为$w$的边。

输出格式

对于每种长度,输出一行,表示答案。

样例

输入样例

3
1 2 3
1 3 9

输出样例

9
3
0

数据范围

对于$30\%$的数据,$n\le 1000$。
对于额外$30\%$的数据,$w\le 100$。
对于$100\%$的数据,$n\le4*10^5$,$1\le u,v\le n$,$w\le 10^6$。

题解

考虑$w\le 100$的数据。

暴力枚举$gcd$,忽略树上$w\nmid gcd$的边。显然答案有单调性,将树的直径的答案更新为当前$gcd$。最后倒着取$\max$即可。

复杂度$O(nw)$。

正解:

还是枚举$gcd$,把$w$为$gcd$的倍数的边建出一个森林,求森林的直径更新即可。

注意到只有当枚举到$w$的约数时,该边才会被加进去。所以复杂度为所有边的约数个数和。

而$10^6$内最大的约数个数只有$128$。

复杂度为跑不满的$O(128n+w\log w)$。$w\log w$是枚举倍数的调和级数。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>

#define maxn 400005
#define inf 0x3f3f3f3f

using namespace std;

inline int read(){
    int x=0,y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
int f[maxn],ans[maxn],h[maxn],num,now,len;
vector<pair<int,int> >ed[1000005];
vector<int>poi;
bool vis[maxn],app[maxn];
struct edge{
    int pre,to;
}e[maxn<<1];
inline void add(int from,int to){
    e[++num].pre=h[from],h[from]=num,e[num].to=to;
}
void dp(int node){
    f[node]=0,vis[node]=1;
    int x;
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(vis[x])continue;
        dp(x);
        len=max(len,f[node]+f[x]+1);
        f[node]=max(f[node],f[x]+1);
    }
}
int main(){
    int n=read(),x,y,z,tp=0;
    for(register int i=1;i<n;++i)x=read(),y=read(),tp=max(z=read(),tp),ed[z].push_back((pair<int,int>){x,y});
    for(register int i=1;i<=tp;++i){
        poi.clear(),num=len=0;
        for(register int j=i;j<=tp;j+=i){
            for(vector<pair<int,int> >::iterator iter=ed[j].begin();iter!=ed[j].end();++iter){
                if(!app[iter->first])poi.push_back(iter->first),app[iter->first]=1,h[iter->first]=0;
                if(!app[iter->second])poi.push_back(iter->second),app[iter->second]=1,h[iter->second]=0;
                add(iter->first,iter->second),add(iter->second,iter->first);
            }
        }
        for(register int j=0;j<poi.size();++j)
            if(!vis[poi[j]])dp(poi[j]);
        for(register int j=0;j<poi.size();++j)app[poi[j]]=vis[poi[j]]=0;
        ans[len]=i;
    }
    for(register int i=n-1;i;--i)ans[i]=max(ans[i],ans[i+1]);
    for(register int i=1;i<=n;++i)
        printf("%d\n",ans[i]);
}