SPOJ 3978 Distance Query
题意:给出一棵有边权的树(100000个点),有100000次讯问两点间路径上的最长边和最短边。
扩展Tarjan算法可以离线解决LCA问题(http://blog.csdn.net/kksleric/article/details/7442258)因此也可用于维护两点间路径上的性质。
设mx[i]为i点到当前集合根节点(tarjan过程中并查集的父亲)路径上的最长边,在合并和路径压缩时根据意义进行相应改变,那么在点a的子树都完成dfs后,所有lca是a的查询
高效求解次小生成树
普通次小生成树的做法首先最小生成树上每个点为根dfs,求出树上任意两点之间的最长边。然后枚举所有不在最小生成树上的边,将其添加到树上必定形成一个环,去掉环上的最长边形成的生成树最小,由此得出(严格)次小生成树。因此复杂度为O(nlogn+n*n)。利用扩展tarjan,求出所有不在最小生成树中的边 之间在最小生成树中路径上的最长边,相当于进行O(n) 次查询,因此复杂度为O(nlogn+n)。
#include
#include
#include
#include
using namespace std;
struct node {
int be, ne, val;
void init(int b, int e, int v) {
be = b;
ne = e;
val = v;
}
};
node buf[200010], query[200010],res[100010];
int Eb[100010], lb, Eq[100010], lq,Er[100010],lres;
void addres(int a,int b,int v){
res[lres].init(b,Er[a],v);
Er[a]=lres++;
}
void addedge(int a, int b, int v) {
buf[lb].init(b, Eb[a], v);
Eb[a] = lb++;
buf[lb].init(a, Eb[b], v);
Eb[b] = lb++;
}
void addquery(int a, int b, int v) {
query[lq].init(b, Eq[a], v);
Eq[a] = lq++;
query[lq].init(a, Eq[b], v);
Eq[b] = lq++;
}
int f[100010], vis[100010];
int mn[100010],mx[100010],inf=1<<28;
int n,ans1[100010],ans2[100010];
int find(int x) {
int temp=f[x];
if (x != temp)
f[x] = find(f[x]);
mn[x]=min(mn[x],mn[temp]);
mx[x]=max(mx[x],mx[temp]);
return f[x];
}
void init() {
lq = lb =lres=0;
for (int i = 1; i <= n; i++) {
f[i] = i;
Er[i]=Eb[i] = Eq[i] = -1;
mx[i]=-inf;
mn[i]=inf;
}
}
void dfs(int a) {
vis[a]=1;
for (int i = Eq[a]; i != -1; i = query[i].ne) {
int b = query[i].be;
if (vis[b] == 1){
int temp=find(b);
addres(temp,a,i);
}
}
for (int i = Eb[a]; i != -1; i = buf[i].ne) {
int b = buf[i].be;
if (vis[b] == 0){
dfs(b);
f[b] =a;
mx[b]=mn[b]=buf[i].val;
}
}
for(int i=Er[a];i!=-1;i=res[i].ne){
int x=res[i].be;
int t=res[i].val;
int y=query[t].be;
find(x);
find(y);
ans1[query[t].val]=min(mn[x], mn[y]);
ans2[query[t].val]=max(mx[x], mx[y]);
}
}
int main(){
int m,a,b,v;
scanf("%d",&n);
init();
for(int i=1;i