題解
最近由於參加NOI集訓,好久沒有更新博客啦ToT
首先上一些部分分的做法:
-
對於前30%的數據,由於,可以直接從每個點開始DFS,記錄路上所有邊權的GCD,更新答案。
複雜度。
-
對於額外30%的數據,由於,可以枚舉路上所有邊權的GCD。GCD確定之後,可能出現在路徑上的邊一定滿足它的權值能被這個GCD整除。所以我們只保留這些邊,然後求森林最長鏈(顯然森林中鏈的長度一定在之間。求出之後用這個GCD更新最長鏈的答案。
注意最終答案的鏈不一定是最長鏈,所以設最長鏈長度爲時GCD最大值爲,則求完數組後要執行以下過程:。複雜度
正解要從第二種部分分思路想。這個思路爲什麼是的?因爲每次求森林最長鏈時其實真正有用的點/邊並不多,而算法將大量時間花費在了無用點(孤立點)上。如果找有用邊時順便記錄有用的點,那麼就只需要遍歷這些點。由於一個數約數的上界是的,所以總時間複雜度,當然實際複雜度比這個上界小很多。
#include<bits/stdc++.h>
using namespace std;
#define FE "walk"
#define fo(i,d,a) for(int i=a.f[d];i;i=a.nxt[i])
const int N=1000005,N2=N<<1;
int n,ans[N],vis[N],mx,fn,cnt,a1;
struct qxx{
int info[N2],f[N],nxt[N2],tot;
void clear(int a){f[a]=0;}
int nw(){return ++tot;}
void pb(int a,int b){int w=nw();info[w]=b,nxt[w]=f[a],f[a]=w;}
}to,e;
int s[N],t[N];
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
void op(int v){if(9<v)op(v/10);putchar((v%10)^48);}
void init();
int find(int v,int fa,int chk){
int b=0;
vis[v]=cnt;fo(i,v,e){
int u=e.info[i];
if(u!=fa){
int c=find(u,v,chk)+1;
fn=max(fn,b+c),b=max(b,c);
}
}return b;
}
void doo(int i,int j){
if(vis[j]!=cnt){
fn=0;
find(j,0,i);
a1=max(a1,fn);
}
}
void solve(){
for(int i=1;i<=mx;i++){
++cnt,a1=0,e.tot=0;
for(int i1=i;i1<=mx;i1+=i)fo(j1,i1,to){
int v=s[to.info[j1]],u=t[to.info[j1]];
e.clear(v),e.clear(u);
}
for(int i1=i;i1<=mx;i1+=i)fo(j1,i1,to){
int v=s[to.info[j1]],u=t[to.info[j1]];
e.pb(v,u),e.pb(u,v);
}
for(int i1=i;i1<=mx;i1+=i)fo(j1,i1,to){
int v=s[to.info[j1]],u=t[to.info[j1]];
doo(i,v),doo(i,u);
}ans[a1]=i;
}
for(int i=n;i;i--)ans[i]=max(ans[i],ans[i+1]);
for(int i=1;i<=n;i++)op(ans[i]),putchar('\n');
}
int main(){
init(),solve();return 0;
}
void init(){
n=rd();for(int i=1;i<n;i++){
int a=rd(),b=rd(),c=rd();mx=max(mx,c);
s[i]=a,t[i]=b,to.pb(c,i);
}
}