题意简述
给定n个节点组成的森林,需要支持两个操作
Q u v:询问泡u和v的最小代价
L u v:连接u和v
对于100%的数据,保证:1<=n<=100000,1<=操作个数m<=200000,1<=Vi<=1000
分析
对于询问,通过树上倍增计算就可以了
对于连接操作,采取启发式合并
将较小的集合连接到较大的集合中去,然后以边的端点为根暴力重构小集合的树
以每个点为研究对象分析
一个点被重构之后,其所在联通快的大小至少扩大为原来的2倍
因此一个点最多被重构log n次
因此所有点合并的复杂度为O(n log n)
总复杂度O((n+m)log n)
代码
#include<cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
const int mn=100000+50,bi=17;
int n,fat[mn],va[mn],u,v,bs,bb[bi+1],bt[mn],tot[mn],f[mn][18],d[mn][bi+1],et,be[mn],ans,m,de[mn];
char ch,cha[10];
struct edge{
int v,ne;
};
edge e[mn*2];
void add_edge(int u,int v){
e[++et].ne=be[u];
e[et].v=v;
be[u]=et;
}
void build_tree(int r,int fa){//dfs建树 求出倍增数组 代表元素 集合大小 深度
fo(i,1,bi){
f[r][i]=f[f[r][i-1]][i-1];
if (!f[r][i])
break;
d[r][i]=d[f[r][i-1]][i-1]+d[r][i-1];
//printf("%d:%d %d\n",i,f[r][i],d[r][i]);输出调试
}
int i=be[r];
while (i){
if (e[i].v!=fa){//递归儿子
de[e[i].v]=de[r]+1;
fat[e[i].v]=fat[r];
f[e[i].v][0]=r;//
d[e[i].v][0]=va[r];
build_tree(e[i].v,r);
tot[r]+=tot[e[i].v];
}
i=e[i].ne;
}
}
int lca(int x,int y){//求lca
int ans=0,dd;
if (de[x]<de[y])
x^=y^=x^=y;
dd=de[x]-de[y];
ans+=va[x];
fo(i,0,bi){
if (dd&bb[i]){
ans+=d[x][i];
x=f[x][i];
}
if (dd<bb[i])
break;
}
if (x==y)
return ans;
ans+=va[y];
for(int i=bi;i>=0;i--)
if ((f[x][i])&&(f[x][i]!=f[y][i])){
ans+=d[x][i];
ans+=d[y][i];
x=f[x][i];
y=f[y][i];
}
return ans+va[f[x][0]];
}
int main(){
fo(i,0,bi)//1..bi
bb[i]=1<<i;
scanf("%d",&n);
fo(i,1,n)
scanf("%d",&va[i]);
fo(i,1,n){
scanf("%d%d",&u,&v);
if (u==0)
bt[++bs]=v;
if (v==0)
bt[++bs]=u;
if ((u)&&(v)){
add_edge(v,u);
add_edge(u,v);
}
}
fo(i,1,bs){
tot[bt[i]]=1;
fat[bt[i]]=bt[i];
de[bt[i]]=1;
build_tree(bt[i],-1);
}
scanf("%d",&m);
fo(i,1,m){
scanf("%s",cha);
scanf("%d%d",&u,&v);
u^=ans;v^=ans;
if (cha[0]=='Q'){
ans=lca(u,v);
printf("%d\n",ans);
}
if (cha[0]=='L'){
add_edge(u,v);
add_edge(v,u);
if (tot[fat[u]]<tot[fat[v]]){
tot[fat[v]]+=tot[fat[u]];
fat[u]=fat[v];
de[u]=de[v]+1;
f[u][0]=v;
d[u][0]=va[v];
build_tree(u,v);
continue;
}
tot[fat[u]]+=tot[fat[v]];
fat[v]=fat[u];
de[v]=de[u]+1;
f[v][0]=u;
d[v][0]=va[u];
build_tree(v,u);
}
}
return 0;
}