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]);
}
}