注意到如果(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