hdu 5398:GCD Tree lct

       注意到如果(x,y)连边,且d=gcd(x,y)使得d<x且d<y,那么考虑(d,x),(d,y),去掉边(x,y)后d必然和一个不在一起,那么连上这条边即可。也就是最后的任意边(x,y)都是x|y或y|x的

      然后从小到大枚举然后lct维护最大生成树即可。(结果暴力可以怒草lct,QAQ)

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 1000000000
#define N 200005
#define M 1100005
#define isrt(x) (c[fa[x]][0]!=x && c[fa[x]][1]!=x)
#define up(x,y) ((a[y]<a[x])?x=(y):0)
using namespace std;

int n,tp,tot,fa[N],c[N][2],q[N],a[N],pos[N],fst[N],pnt[M],nxt[M],ans[N];
bool rev[N];
void add(int x,int y){
    pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void maintain(int x){
    pos[x]=x; up(pos[x],pos[c[x][0]]); up(pos[x],pos[c[x][1]]);
}
void rotate(int x){
    int y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1;
    if (!isrt(y))
        if (c[z][0]==y) c[z][0]=x; else c[z][1]=x;
    fa[x]=z; fa[y]=x; fa[c[x][r]]=y;
    c[y][l]=c[x][r]; c[x][r]=y;
    maintain(y);
}
void pushdn(int x){
    if (rev[x]){
        swap(c[x][0],c[x][1]);
        rev[c[x][0]]^=1; rev[c[x][1]]^=1; rev[x]=0;
    }
}
void splay(int x){
    int i,y,z;
    for (i=x,tp=0; !isrt(i); i=fa[i]) q[++tp]=i; q[++tp]=i;
    while (tp) pushdn(q[tp--]);
    for (; !isrt(x); rotate(x)){
        y=fa[x]; z=fa[y];
        if (!isrt(y)) rotate((c[z][0]==y ^ c[y][0]==x)?x:y);
    }
	maintain(x);
}
void acss(int x){
    int y=0;
    for (; x; y=x,x=fa[x]){
        splay(x); c[x][1]=y; maintain(x);
    }
}
void makert(int x){ acss(x); splay(x); rev[x]^=1; }
void link(int x,int y){ makert(x); fa[x]=y; }
int main(){
    int i,j,k,x,y;
    for (i=1; i<=50000; i++)
        for (j=i<<1; j<=100000; j+=i) add(j,i);
    a[0]=a[n=2]=inf;
    for (i=2; i<=100000; i++){
        a[++n]=pnt[fst[i]]; a[++n]=inf;
        ans[i]=ans[i-1]+a[n-1];
        link(n,n-1); link(n-1,a[n-1]<<1);
        for (j=nxt[fst[i]]; j; j=nxt[j]){
            k=pnt[j]; y=k<<1;
            makert(n); acss(y); splay(y);
            if (a[x=pos[y]]<k){
                ans[i]+=k-a[x];
                splay(x);
                fa[c[x][0]]=fa[c[x][1]]=0;
                c[x][0]=c[x][1]=0; pos[x]=x;
                a[x]=k; link(x,y); link(n,x);
            }
        }
    }
    while (~scanf("%d",&i)) printf("%d\n",ans[i]);
    return 0;
}


by lych

2016.11.13

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值