hdu5398(lct维护最大生成树)

lct维护最大生成树,维护最小生成树,参考别人的代码理解的,但是不会证明,不明白为什么要这样做就可以得到最大生成树。

至于为什么只选择一个点是另一个点的整倍数的边,我的理解是:把每条边的权值取相反数,那样求最大生成树,就转化为求最小生成树;假设现在一共有m个点,边的最小权值是-n,很明显n<m,在求最小生成树中,根据kruskal算法来说,先选择一条权值最小的边,-n,此时,我们选(n,2n)这两个点构成的边,下面,如果3n<=m,4n<=m……的话,就将(n,3n),(n,4n),选进来,一直到n的某个倍数>m,那么此时便不存在权值为-n的安全边,接下来选权值为-(n-1)的边,同上,不过要注意选(n-1,n的倍数)的边是,要选择安全边,按照权值依次选择下去,最终选择到权值为-1的边,那么此时便可以构成最小生成时,在将边的权值取反,恢复到正值,此时便可以得到最大生成树,所以在集合{一个点是另一个点的整数倍的边}中可以构成一个最大生成树。


代码参考:http://blog.csdn.net/blankcqk/article/details/47759541


放错代码了,放成参考代码了,现在已经改成我自己从写的代码了。


(感觉夏天就是要和冰冰凉凉的桔子汁,就是用桔子粉泡的那种,http://s.dianping.com/topic/2988138,暴露年纪了惊恐)

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
using namespace std;
#define N 100010
#define INF 0x3f3f3f3f

struct node{
    node *ch[2];//左右孩子节点地址
    node *fa;//父节点的地址,但是不同于普通树中父节点的含义
    int own;//点的话,等于多少无所谓,所以初始化为0后可不再修改,如果是边的话,就是大的那个数
    int mingcd;//点的话初始化为INF,如果是边的话,就初始化为两个点的gcd
    node *minnode;//要初始化为自己,它所在的splay树中,以它为根的节点中,mingcd最小的
    int rev;//要初始化为0,该节点处孩子节点处于正确情况下,孩子节点的左右孩子节点是否需要交换

    void init(){//初始化,如果是点的话就不需要再进行任何修改了,但是如果是边的话,要看情况修改mingcd和own
        ch[0]=NULL;
        ch[1]=NULL;
        fa=NULL;
        mingcd=INF;
        minnode=this;
        rev=0;
        own=0;

        return;
    }

    bool isroot(){//判断this这个节点是不是它所在splay树的根节点,并不是判断它是不是整棵树的根节点
        return fa==NULL||((fa->ch[0]!=this)&&(fa->ch[1]!=this));
    }

    int dir(){//使用这个函数前需确定这个点不是它所在的splay树的根节点,并且标志没有积累
        return fa->ch[1]==this?1:0;
    }

    void setn(int d,node *child){//把child设为this的d孩子,d=1时,右孩子,d=0时,左孩子
        ch[d]=child;
        if(child){
           child->fa=this;
        }

        return;
    }

    void push_up(){//向上总结,这时即使有些标志没有向下传,也不影响
        minnode=this;

        if(ch[0]&&(minnode->mingcd>ch[0]->minnode->mingcd)){
            minnode=ch[0]->minnode;
        }
        if(ch[1]&&(minnode->mingcd>ch[1]->minnode->mingcd)){
            minnode=ch[1]->minnode;
        }

        return;
    }

    void rot(){//旋转,前提是这个点不是它所在splay树的根节点,并且标志没有积累
        int d=dir();
        node *tempfafa=fa->fa;

        if(!(fa->isroot())){
            tempfafa->ch[fa->dir()]=this;
        }
        fa->setn(d,ch[!d]);
        setn(!d,fa);
        fa=tempfafa;

        ch[!d]->push_up();

        return;
    }

    void fswitch(){//交换左右孩子,并且标志左右孩子的孩子要交换。
        swap(ch[0],ch[1]);
        rev^=1;
    }

    void push_down(){//标记下传
        if(rev){
            if(ch[0]){
                ch[0]->fswitch();
            }
            if(ch[1]){
                ch[1]->fswitch();
            }
        }

        rev=0;

        return;
    }

    void go(){
        if(!isroot()){
            fa->go();
        }
        push_down();

        return;
    }

    void splay(){
        go();

        while(!isroot()){
            if(!(fa->isroot())){
                dir()==fa->dir()?fa->rot():rot();
            }
            rot();
        }
        push_up();

        return;
    }

    void access(){
        for(node *p=this,*q=NULL;p!=NULL;q=p,p=p->fa){
            p->splay();
            p->setn(1,q);
            p->push_up();
        }
        splay();

        return;
    }

    void cut(){//无论此时整棵树的根节点是谁,access和此时它的父节点cut,所以不一定cut哪条边
        access();
        ch[0]->fa=NULL;
        ch[0]=NULL;
        push_up();
    }

    void make_root(){
        access();
        fswitch();
    }

    void cut(node *another){//此时明确要求cut this和another这条边
        make_root();
        another->cut();
    }

    void link(node *another){//printf("wo shi da hao ren");
        /*another->make_root();
        another->fa=this;
        another->access();*///可有可无
        make_root();
        fa=another;
    }

    node *query(node *another){
        make_root();
        another->access();
        return another->minnode;

        /*another->make_root();
        access();

        return minnode;*/
    }
};

long long int ans[N];
vector<int> ver[N];
node *tree[N],pool[2*N],*tail;

node *newnode(){
    tail->init();
    tail++;

    return tail-1;
}

void init(int n){
    tail=pool;

    for(int i=1;i<=n;i++){
        tree[i]=newnode();
        tree[i]->own=i;
    }

    for(int i=n;i>=1;i--){
        for(int j=i+i;j<=n;j=j+i){
            ver[j].push_back(i);
        }
    }

    long long int tempans=0;
    for(int i=2;i<=n;i++){
        node *p=newnode();
        int temp=ver[i][0];
        p->own=i;
        p->mingcd=temp;
        p->link(tree[i]);
        p->link(tree[temp]);
        tempans=tempans+temp;

        /*if(i==4){
            node *temptree=tree[4]->query(tree[2]);
            printf("%d %d %d\n",temptree->own,temptree->mingcd,temptree->mingcd);
        }*/


        for(int j=1;j<ver[i].size();j++){
            int temp=ver[i][j];
            node *temptree=tree[temp]->query(tree[i]);
            /*if(temp==1000){
                printf("%d %d %d %d %d %d\n",i,j,temp,temptree->own,temptree->mingcd,temptree->mingcd);
            }*///错误导致tree[x]->query(tree[y])总是选中tree[y]代表点的这个节点

            if(temptree->mingcd<temp){
                /*if(i==24){
                    printf("%d %d\n",i,temptree->mingcd);
                }*/
                tempans=tempans-temptree->mingcd;
                tempans=tempans+temp;
                int t1=temptree->own;
                int t2=temptree->mingcd;
                temptree->cut(tree[t1]);
                temptree->cut(tree[t2]);
                temptree->init();
                temptree->mingcd=temp;
                temptree->own=i;
                temptree->link(tree[i]);
                temptree->link(tree[temp]);
            }
        }
        ans[i]=tempans;
    }

    return;
}

int main(){
    int n;

    n=100000;
    init(n);
    while(scanf("%d",&n)!=EOF){
        printf("%lld\n",ans[n]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值