Walk

题目大意

给定一颗边权树,对于任意i求出f[i]表示一条长度为i的简单路径gcd的最大值。

瞎做

我们可以把每条边挂在其的约数上,使用质因数分解。
对每个gcd做一下,对保留的所有边构出新树,然后对于所有这些边涉及到的点形成的每个联通块用两遍dfs求直径,最终得到森林的最长链。
设最长链为k,当前枚举的gcd为v,则 f[k]=max(f[k],v)
这样做显然会错,但我们注意到 f[i]>=f[i+1]
倒着取max一下就是对的了
注意卡常

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=400000+10,maxd=1000000,maxtot=15000000;
int h[maxn],go[maxn*2],next[maxn*2],a[maxn][2];
int h2[maxd+10],g2[maxtot+10],n2[maxtot+10];
int h3[maxn],g3[maxn*2],n3[maxn*2];
int fa[maxn],rank[maxn],data[maxn][3];
int dfn[maxn*2],fi[maxn],d[maxn],rmq[maxn*2][25],zjy[maxn*2],two[25];
int pri[maxd+10],b[20],c[20],flag[maxn],dx[maxn];
bool pd[maxd+10],czy[maxn];
int sta[maxn],f[maxd+10];
bool bz[maxn];
int i,j,k,l,r,t,n,m,cnt,top,tot,num,sum,mx,ans,ans2,ans3,wdc,gjx;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void prepare(){
    int i;
    fo(i,2,maxd){
        if (!pd[i]) pri[++num]=i;
        fo(j,1,num){
            if ((ll)i*pri[j]>maxd) break;
            pd[i*pri[j]]=1;
            if (i%pri[j]==0) break;
        }
    }
}
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void add2(int x,int y){
    g2[++cnt]=y;
    n2[cnt]=h2[x];
    h2[x]=cnt;
}
void gao(int x,int y,int id){
    if (x==sum+1){
        add2(y,id);
        return;
    }
    int i,j=1;
    fo(i,0,c[x]){
        gao(x+1,y*j,id);
        j*=b[x];
    }
}
void link(int id,int v){
    int i;
    sum=0;
    fo(i,1,num){
        if (pri[i]*pri[i]>v) break;
        if (v%pri[i]==0){
            b[++sum]=pri[i];
            c[sum]=0;
            while (v%pri[i]==0){
                v/=pri[i];
                c[sum]++;
            }
        }
    }
    if (v>1){
        b[++sum]=v;
        c[sum]=1;
    }
    gao(1,1,id);
}
void dfs(int x,int y){
    //d[x]=d[y]+1;
    //dfn[++top]=x;
    //fi[x]=top;
    int t=h[x];
    while (t){
        if (go[t]!=y){
            dfs(go[t],x);
            //dfn[++top]=x;
        }
        t=next[t];
    }
}
void dfs1(int x,int y){
    bz[x]=1;
    d[x]=d[y]+1;
    if (!ans2||d[x]>d[ans2]) ans2=x;
    int t=h3[x];
    while (t){
        if (g3[t]!=y) dfs1(g3[t],x);
        t=n3[t];
    }
}
void dfs2(int x,int y){
    d[x]=d[y]+1;
    if (!ans3||d[x]>d[ans3]) ans3=x;
    int t=h3[x];
    while (t){
        if (g3[t]!=y) dfs2(g3[t],x);
        t=n3[t];
    }
}
void add3(int x,int y){
    g3[++gjx]=y;
    n3[gjx]=h3[x];
    h3[x]=gjx;
}
void work(int v){
    int i,j,k,l,r,t;
    ans=wdc=gjx=0;
    r=h2[v];
    while (r){
        t=g2[r];
        j=a[t][0];k=a[t][1];
        if (!czy[j]) dx[++wdc]=j,h3[j]=0,czy[j]=1;
        if (!czy[k]) dx[++wdc]=k,h3[k]=0,czy[k]=1;
        add3(j,k);add3(k,j);
        r=n2[r];
    }
    fo(i,1,wdc) bz[dx[i]]=0;
    fo(i,1,wdc)
        if (!bz[dx[i]]){
            ans2=ans3=0;
            dfs1(dx[i],0);
            dfs2(ans2,0);
            if (d[ans3]>ans) ans=d[ans3];
        }
    f[ans]=v;
    fo(i,1,wdc) czy[dx[i]]=0;
}
void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top){
        putchar('0'+sta[top]);
        top--;
    }
    putchar('\n');
}
int main(){
    freopen("walk.in","r",stdin);freopen("walk.out","w",stdout);
    n=read();
    prepare();
    fo(i,1,n-1){
        j=read();k=read();l=read();
        mx=max(mx,l);
        add(j,k);add(k,j);
        a[i][0]=j;a[i][1]=k;
        link(i,l);
    }
    dfs(1,0);
    d[0]=-1;
    fo(i,1,mx) 
        if (h2[i]) work(i);
    fd(i,n-1,1) f[i]=max(f[i],f[i+1]);
    fo(i,1,n) write(f[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值