hdu 5398 GCD Tree

本文介绍了一种在最大生成树基础上寻找更优解的方法。通过对每个节点的因数进行枚举,利用Kruskal算法流程确保加入的新边能形成更优生成树。使用链接切割树(LCT)进行高效更新,最终复杂度达到O(n√nlogn)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

显然应当预处理出所有的答案,考虑在n1的最大生成树上加边得到n的最大生成树,即对每一条边uv,在链uv上找到值最小的边并删除,根据Kruskal的流程,这个操作得到的必然是一个更优的解。
考虑对于n的一个因数d,满足(a,n)=da必然为a=dp|(p,n)=1,设所有满足(ai,n)=daidp1,dp2...dpϕ(nd)。在最大生成树中,连接了所有dpi的分量上的所有边的权值都应当不小于d,因为如果有一条边小于d,考虑Kruskal流程,其连接的两个子树应当由(dpi,dpj)来连接((dpi,dpj)d)。也就是说,如果连接一条(n,dpi),并能够通过删除某边得到更优解,那么这个操作只能进行一次(第二次连接必然构成了一个边权皆不小于d的环)且任意dpi都是满足要求的(因为可删除的边在连接了所有dpi的分量的外面)。
所以每次枚举当前n的所有因数连边,之后让lct做一点微小的工作即可,复杂度O(nnlogn)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<climits>
#include<cstdio>
//--container
using namespace std;
#include<stack>
typedef long long ll;
const int up=1000000;
//--
#define clr(a) memset(a,0,sizeof(a))
struct nd{nd* fa,* gfa,* cd[2];int mi,wg;bool fg;}ndr[300010],*id[100010];
struct eg{int v,nx;}gp[1200000];
ll rs[100010],ts=0;int n,tn,cnt,hd[100010];stack<nd*>sk;
void psh(int u,int v){
    ++cnt;gp[cnt].v=v;
    gp[cnt].nx=hd[u],hd[u]=cnt;
};
nd* _new(){
    nd* t;if(!sk.empty()){
        t=sk.top();sk.pop();t->cd[0]=t->cd[1]=t->fa=t->gfa=0,t->fg=0;
        return t;
    };
    ndr[tn].cd[0]=ndr[tn].cd[1]=ndr[tn].fa=ndr[tn].gfa=0,ndr[tn].fg=0;
    return &ndr[tn++];
};
void _delete(nd* t){sk.push(t);};
inline void mis(nd* v){
    v->mi=min(v->wg,min(v->cd[0]?v->cd[0]->mi:up,v->cd[1]?v->cd[1]->mi:up));
};
void fgs(nd* v){
    if(v->cd[0])v->cd[0]->fg^=1;
    if(v->cd[1])v->cd[1]->fg^=1;
    swap(v->cd[0],v->cd[1]),v->fg=0;
};
void tgs(nd* v){if(v->fa){tgs(v->fa);v->gfa=v->fa->gfa;}if(v->fg)fgs(v);};
void rot(nd* v){
    int i,j,d;nd* f=v->fa,* g=f->fa;d=f->cd[0]==v?0:1;
    if(g)g->cd[g->cd[0]==f?0:1]=v;v->fa=g,f->fa=v;
    if(v->cd[d^1])v->cd[d^1]->fa=f;f->cd[d]=v->cd[d^1],v->cd[d^1]=f;mis(f),mis(v);
};
void spy(nd* v,nd* x){
    int i,j,a,b;nd* f,* g;for(tgs(v);v->fa!=x;){
        f=v->fa,g=f->fa;
        if(x==g){rot(v);break;};
        a=f->cd[0]==v?0:1,b=g->cd[0]==f?0:1;
        if(a==b)rot(f),rot(v);
        else
            rot(v),rot(v);
    }
};
void access(nd* v){
    nd* d,* k;for(d=0;v;){
        spy(v,0);if(v->cd[1])v->cd[1]->fa=0,v->cd[1]->gfa=v;
        v->cd[1]=d;if(d)d->fa=v;mis(v),d=v,v=v->gfa;
    }
};
void mkrot(nd* v){access(v);spy(v,0);v->gfa=0,v->fg^=1;};
nd* _dfs(nd* v,int x){
    if(v->wg==x)return v;
    if(v->cd[0]&&v->cd[0]->mi==x)return _dfs(v->cd[0],x);
    return _dfs(v->cd[1],x);
};
nd* _ldfs(nd* v){
    if(v->fg)fgs(v);
    return v->cd[1]?_ldfs(v->cd[1]):v;
};
nd* _rdfs(nd* v){
    if(v->fg)fgs(v);
    return v->cd[0]?_rdfs(v->cd[0]):v;
};
nd* _fl(nd* v){return v->cd[0]?_ldfs(v->cd[0]):v->fa;};
nd* _fr(nd* v){return v->cd[1]?_rdfs(v->cd[1]):v->fa;};
void _cut(nd* v){
    if(v->fg)fgs(v);spy(v,0);nd* l=_fl(v),* r=_fr(v);
    spy(l,v);spy(r,v);l->fa=r->fa=l->gfa=r->gfa=0;
    _delete(v);
};
void _link(nd* u,nd* v,int x){
    nd* d=_new();d->mi=d->wg=x;
    access(u);spy(u,0);d->gfa=u;
    mkrot(v);v->gfa=d;access(v);spy(d,0);
};
void _cl(nd* u,nd* v,int x){
    int t;mkrot(u);access(v);spy(v,0);
    if(v->mi>=x)return;
    ts+=(x-v->mi);_cut(_dfs(v,v->mi));_link(u,v,x);
};
void clit(){
    int i,j,d;bool fg;for(cnt=0,i=1;i<=100000;++i){
        for(j=i;j<=100000;j+=i)psh(j,i);
    }
    for(i=1,ts=0;i<=100000;++i){
        id[i]=_new();id[i]->mi=id[i]->wg=up;
        if(i>1){
            j=gp[hd[i]].nx;ts+=gp[j].v;
            _link(id[i],id[gp[j].v],gp[j].v);
            for(j=gp[j].nx;j;j=gp[j].nx)
                _cl(id[i],id[gp[j].v],gp[j].v);
        }
        rs[i]=ts;
    }
};
bool cl(){
    int n;if(scanf("%d",&n)==-1)return 0;
    printf("%d\n",rs[n]);
    return 1;
};
int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    for(clit();cl(););
    return 0;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值