NOIp2016十連測第五場T2 walk 題解

傳送門

題解

最近由於參加NOI集訓,好久沒有更新博客啦ToT

首先上一些部分分的做法:

  • 對於前30%的數據,由於N1000N\le1000,可以直接從每個點開始DFS,記錄路上所有邊權的GCD,更新答案。

    複雜度O(n2)O(n^2)

  • 對於額外30%的數據,由於w100w\le100,可以枚舉路上所有邊權的GCD。GCD確定之後,可能出現在路徑上的邊一定滿足它的權值能被這個GCD整除。所以我們只保留這些邊,然後求森林最長鏈(顯然森林中鏈的長度一定在(0,最長鏈](0,\text{最長鏈}]之間。求出之後用這個GCD更新最長鏈的答案。

    注意最終答案的鏈不一定是最長鏈,所以設最長鏈長度爲ii時GCD最大值爲ans[i]ans[i],則求完ansans數組後要執行以下過程:for i=n1  1 do ans[i]=max(ans[i],ans[i+1])for~i=n-1~\to~1~do~ans[i]=\max(ans[i],ans[i+1])。複雜度O(n×max(w))O(n\times\max(w))。

正解要從第二種部分分思路想。這個思路爲什麼是O(n×max(w))O(n\times\max(w))的?因爲每次求森林最長鏈時其實真正有用的點/邊並不多,而算法將大量時間花費在了無用點(孤立點)上。如果找有用邊時順便記錄有用的點,那麼就只需要遍歷這些點。由於一個數ii約數的上界是O(i)O(\sqrt i)的,所以總時間複雜度O(max(w)32)O(\max(w)^\frac 32),當然實際複雜度比這個上界小很多。

#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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章