题目链接:hdu 5398 GCD Tree
题意:
给你一个n,让你输出一个最大生成树的价值。
有n个点,任意两点有条边,边权为gcd(u,v)。
题解:
由于题意要让你输出所有的1e5内所有的n,所以只能动态维护最大生成树。(LCT可以做到)
显然可能有用的边只能是x向x的因子连的边。
LCT如何来维护最大生成树?一个最简单的办法就是将边变成点,然后用点来记录权值。
然后每次加入一条边(u,v)的时候,LCT查询当前树中的(u,v)链上的最小值val,如果val比加入的边的权值小,
那么就把这条边删掉,然后加入(u,v)。
然后对于这题,可以先将全部的点连向1,然后再来加边,每加入一个点,记录一下答案就行了。
1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;i++) 3 #define mst(a,b) memset(a,b,sizeof(a)) 4 using namespace std; 5 typedef pair<int,int>P; 6 namespace LCT 7 { 8 const int N=2e5+7; 9 int f[N],son[N][2],tmp[N],lazy[N];bool rev[N]; 10 P sum[N],val[N],eg[N]; 11 bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;} 12 void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;} 13 void add(int x,P c){if(!x)return;val[x]=sum[x]=c;} 14 void pb(int x){if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;} 15 void up(int x){ 16 sum[x]=val[x]; 17 if(son[x][0]) 18 { 19 if(sum[son[x][0]].first<sum[x].first)sum[x]=sum[son[x][0]]; 20 } 21 if(son[x][1]) 22 { 23 if(sum[son[x][1]].first<sum[x].first)sum[x]=sum[son[x][1]]; 24 } 25 } 26 void rotate(int x){ 27 int y=f[x],w=son[y][1]==x; 28 son[y][w]=son[x][w^1]; 29 if(son[x][w^1])f[son[x][w^1]]=y; 30 if(f[y]){ 31 int z=f[y]; 32 if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x; 33 } 34 f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y); 35 } 36 void splay(int x){ 37 int s=1,i=x,y;tmp[1]=i; 38 while(!isroot(i))tmp[++s]=i=f[i]; 39 while(s)pb(tmp[s--]); 40 while(!isroot(x)){ 41 y=f[x]; 42 if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);} 43 rotate(x); 44 } 45 up(x); 46 } 47 void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);} 48 int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;} 49 void makeroot(int x){access(x);splay(x);rev1(x);} 50 void link(int x,int y){makeroot(x);f[x]=y;access(x);} 51 void cutf(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);} 52 void cut(int x,int y){makeroot(x);cutf(y);} 53 P ask(int x,int y){makeroot(x);access(y);splay(y);return sum[y];} 54 } 55 using namespace LCT; 56 vector<int>g[N]; 57 long long ans[N]; 58 59 void init() 60 { 61 F(i,2,100000) 62 for(int j=i+i;j<=100000;j+=i) 63 g[j].push_back(i); 64 F(i,1,100000)sum[i]=val[i]=P(N,N); 65 F(i,2,100000) 66 { 67 ans[i]=ans[i-1]; 68 int nd=100000+i; 69 link(nd,1);add(nd,P(1,nd));link(i,nd); 70 eg[nd]=P(1,i),ans[i]++; 71 for(auto &it:g[i]) 72 { 73 P now=ask(i,it); 74 if(it<=now.first)continue; 75 cut(eg[now.second].first,now.second); 76 cut(now.second,eg[now.second].second); 77 ans[i]+=it-now.first; 78 link(now.second,i),add(now.second,P(it,now.second)); 79 link(it,now.second),eg[now.second]=P(i,it); 80 } 81 } 82 } 83 84 int main() 85 { 86 init(); 87 for(int n;~scanf("%d",&n);printf("%lld ",ans[n])); 88 return 0; 89 }