uva11354 最小瓶颈路及其应用

来源:互联网 发布:快手下载软件 编辑:程序博客网 时间:2024/06/09 23:46

关键词:LCA,最小瓶颈路,树上路径问题、次小生成树

询问任意两点间最小瓶颈路(最大边最小)

法一:

1.dp[u][v]:MST上u,v路径最小瓶颈路 O(n^2)

2.每组询问输出dp[u][v] O(q)

本题n最大可取50000,此法不可取

法二:

1.anc[u][i]:MST有根树中,u的第2^i个祖先;maxcost[u][i]:u到第2^i个祖先路径上的瓶颈(最长边) O(n*logn)

2.每组询问,查找u和v分别到它们公共祖先路径上的瓶颈的最大值 O(logn*q)

询问复杂度上升,但是可以接受。

心得:树上路径问题。先转化为有根树,再用LCA解决多组询问问题(前提是一般dp的预处理复杂度较高,无法完成,而LCA预处理复杂度较低

应用:边权减小,询问MST变化(n很大,无法O(n^2)预处理)


变题:给定一棵树,每个顶点有一个权值,询问某两点路径上的最大权值(n<=50000,q<=100000)

1.建立有根树,边权定义为子节点的点权,只有根节点附加权值 O(n)

2.同上求anc[u][i]和maxcost[u][i] O(n*logn)

3.询问时,加一个特判,如果两点公共祖先是根节点,则需要比较ans和根节点权值的大小,取较大值即可 O(q*logn)



#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>#include<vector>#include<queue>#include<math.h>#define ll long long#define sf scanf#define pf printf#define INF 1<<29#define mem(a,b) memset(a,b,sizeof(a))#define lowbit(x) x&(-x)#define maxn 50010#define maxm 100010const ll mol=1000000007;using namespace std;int n,m,q;struct Node{    int u,v,w;    bool operator<(const Node&rhs)const{        return w<rhs.w;    }}node[maxm<<1];struct Edge{    int to,next,w;}edge[maxn<<2];int head[maxn],tot,mst;int p[maxn];int father[maxn],depth[maxn],cost[maxn];int anc[maxn][30],maxcost[maxn][30];void add(int u,int v,int w){    edge[tot].to=v,edge[tot].w=w,edge[tot].next=head[u],head[u]=tot++;}int Find(int x){ return (p[x]==x)?x:Find(p[x]); }void kruscal(){    for(int i=1;i<=n;i++) p[i]=i;    sort(node+1,node+m+1);    mem(head,-1),tot=0,mst=0;    for(int i=1;i<=m;i++){        int u=node[i].u,v=node[i].v,w=node[i].w;        int x=Find(u),y=Find(v);        if(x!=y){            p[x]=y;mst+=w;            add(u,v,w),add(v,u,w);        }    }}void root(int u,int fa,int w){    father[u]=fa,cost[u]=w;    if(fa!=-1)  depth[u]=depth[fa]+1;    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].to,w=edge[i].w;        if(v!=fa) root(v,u,w);    }}void preprocess(){    for(int i=1;i<=n;i++){//大括号位置打错了TAT...        anc[i][0]=father[i],maxcost[i][0]=cost[i];        for(int j=1;(1<<j)<=n;j++) anc[i][j]=-1;    }        for(int j=1;(1<<j)<=n;j++)            for(int i=1;i<=n;i++)                if(anc[i][j-1]!=-1){//判断条件很重要!                    anc[i][j]=anc[anc[i][j-1]][j-1];                    maxcost[i][j]=max(maxcost[i][j-1],maxcost[anc[i][j-1]][j-1]);                }}int query(int u,int v){    if(depth[u]<depth[v]) swap(u,v);//交换u,v,而不是交换u,v的深度    int log,ans=-INF;//记录u深度的二进制最大位数    for(log=0;(1<<log)<=depth[u];log++); log--;    for(int i=log;i>=0;i--)//上升到同一高度        if(depth[u]-(1<<i)>=depth[v]) { ans=max(ans,maxcost[u][i]);u=anc[u][i]; }    if(u==v) return ans;//v是u的祖先---特判!!!    for(int i=log;i>=0;i--)//同步上升到公共祖先节点的子节点        if(anc[u][i]!=-1&&anc[u][i]!=anc[v][i]){            ans=max(ans,maxcost[u][i]),u=anc[u][i];            ans=max(ans,maxcost[v][i]),v=anc[v][i];        }    ans=max(ans,cost[u]),ans=max(ans,cost[v]);    return ans;}int main(){    //freopen("a.txt","r",stdin);    int t=0;    while(scanf("%d%d",&n,&m)!=EOF){        if(t!=0) printf("\n");//输出问题---最后一组不能输出换行,换行必须写在前面        t++;        for(int i=1;i<=m;i++) scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].w);        kruscal();        depth[1]=0; root(1,-1,0);        preprocess();        scanf("%d",&q);        while(q--){            int u,v; scanf("%d%d",&u,&v);            printf("%d\n",query(u,v));        }    }}


利用最小瓶颈路结论可以求出次小生成树

O(nlogn)预处理,O(mlogn)询问

#include<stdio.h>#include<string.h>#include<iostream>#include<vector>#include<queue>#include<algorithm>#define maxm 1000000#define maxn 110#define rad 10000#define INF 1<<28#define ll long long#define rad 10000#define mem(a,b) memset(a,b,sizeof(a))using namespace std;int t,n,m,mst;struct Node{    int u,v,w;    bool operator<(const Node&rhs)const{        return w<rhs.w;    }}node[maxn*maxn];struct Edge{    int to,next,w;}edge[maxn*maxn];int head[maxn],tot,p[maxn],vis[maxn][maxn],fa[maxn],cost[maxn];int maxc[maxn][20],maxc2[maxn][20],anc[maxn][20],depth[maxn];void add(int u,int v,int w) { edge[tot].to=v,edge[tot].next=head[u],edge[tot].w=w,head[u]=tot++; }int Find(int u) { return p[u]==u?u:(p[u]=Find(p[u])); }void kruscal(){    for(int i=1;i<=n;i++) p[i]=i;    mst=0,mem(vis,0),mem(head,-1),tot=0;    for(int i=1;i<=m;i++){        int x=Find(node[i].u),y=Find(node[i].v);        if(x!=y){            p[x]=y,mst+=node[i].w;            vis[node[i].u][node[i].v]=vis[node[i].v][node[i].u]=1;            add(node[i].u,node[i].v,node[i].w),add(node[i].v,node[i].u,node[i].w);        }    }}void dfs(int u,int f,int w){    cost[u]=w,fa[u]=f,depth[u]=(f==-1)?0:(depth[f]+1);    for(int i=i=head[u];i!=-1;i=edge[i].next)        if(edge[i].to!=f) dfs(edge[i].to,u,edge[i].w);}void process(){    for(int i=1;i<=n;i++){        anc[i][0]=fa[i],maxc[i][0]=cost[i],maxc2[i][0]=cost[i];        for(int j=1;(1<<j)<n;j++) anc[i][j]=-1;    }    for(int j=1;(1<<j)<n;j++)        for(int i=1;i<=n;i++)            if(anc[i][j-1]!=-1){                int a=anc[i][j-1];                anc[i][j]=anc[a][j-1];                maxc[i][j]=max(maxc[i][j-1],maxc[a][j-1]);            }}int query(int u,int v){    int tmp,log,i;    if(depth[u]<depth[v]) swap(u,v);    for(log=1;(1<<log)<=depth[u];log++); log--;    int ans=-INF;    for(int i=log;i>=0;i--) if(depth[u]-depth[v]>=(1<<i)) { ans=max(ans,maxc[u][i]);u=anc[u][i]; }    if(u==v) return ans;    for(int i=log;i>=0;i--)        if(anc[u][i]!=-1&&anc[u][i]!=anc[v][i]){            ans=max(maxc[u][i],ans),u=anc[u][i];            ans=max(maxc[v][i],ans),v=anc[v][i];        }    ans=max(ans,cost[u]),ans=max(ans,cost[v]);    return ans;}int main(){    //freopen("a.txt","r",stdin);    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m);        for(int i=1;i<=m;i++)            scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].w);        sort(node+1,node+m+1);        kruscal();        dfs(1,-1,0);//转化为有根树        process();//倍增法预处理x的第i个祖先及路径上的最长边和次长边        int ans=INF;        for(int i=1;i<=m;i++){            int u=node[i].u,v=node[i].v,w=node[i].w;            if(!vis[u][v]){                int tmp=mst+w-query(u,v);                if(tmp<ans) ans=tmp;            }        }        printf("%d %d\n",mst,ans);    }}

求出最长边和严格次长边解决严格次小生成树问题

#include<stdio.h>#include<string.h>#include<iostream>#include<vector>#include<queue>#include<algorithm>#define maxm 500000#define maxn 100100#define rad 10000#define INF 1<<28#define ll long long#define rad 10000#define mem(a,b) memset(a,b,sizeof(a))using namespace std;int t,n,m,mst;struct Node{    int u,v,w;    bool operator<(const Node&rhs)const{        return w<rhs.w;    }}node[maxm<<1];struct Edge{    int to,next,w;}edge[maxm<<1];int head[maxn],tot,p[maxn],fa[maxn],cost[maxn];int maxc[maxn][20],maxc2[maxn][20],anc[maxn][20],depth[maxn];int max1,max2;bool vis[maxm];void add(int u,int v,int w) { edge[tot].to=v,edge[tot].next=head[u],edge[tot].w=w,head[u]=tot++; }int Find(int u) { return p[u]==u?u:(p[u]=Find(p[u])); }void kruscal(){    for(int i=1;i<=n;i++) p[i]=i;    mst=0,mem(vis,0),mem(head,-1),tot=0;    for(int i=1;i<=m;i++){        int x=Find(node[i].u),y=Find(node[i].v);        if(x!=y){            p[x]=y,mst+=node[i].w,vis[i]=1;            add(node[i].u,node[i].v,node[i].w),add(node[i].v,node[i].u,node[i].w);        }    }}void dfs(int u,int f,int w){    cost[u]=w,fa[u]=f,depth[u]=(f==-1)?0:(depth[f]+1);    for(int i=i=head[u];i!=-1;i=edge[i].next)        if(edge[i].to!=f) dfs(edge[i].to,u,edge[i].w);}void process(){    for(int i=1;i<=n;i++){        anc[i][0]=fa[i],maxc[i][0]=cost[i],maxc2[i][0]=0;        for(int j=1;(1<<j)<n;j++) anc[i][j]=-1;    }    for(int j=1;(1<<j)<n;j++)        for(int i=1;i<=n;i++)            if(anc[i][j-1]!=-1){                int a=anc[i][j-1];                anc[i][j]=anc[a][j-1];                maxc[i][j]=max(maxc[i][j-1],maxc[a][j-1]);                if(maxc[i][j-1]==maxc[a][j-1]) maxc2[i][j]=max(maxc2[i][j-1],maxc2[a][j-1]);                else if(maxc[i][j-1]>maxc[a][j-1]) maxc2[i][j]=max(maxc2[i][j-1],maxc[a][j-1]);                else maxc2[i][j]=max(maxc[i][j-1],maxc2[a][j-1]);            }}void cal(int u,int i){    u=anc[u][i],max1=max(max1,maxc[u][i]);    if(max1==maxc[u][i]) max2=max(max2,maxc2[u][i]);    else if(max1<maxc[u][i]) max2=max(max1,maxc2[u][i]);    else max2=max(max2,maxc[u][i]);}void cal2(int x){    if(x>max2&&x<max1) max2=x;    if(x>max1) max2=max1,max1=x;}void query(int u,int v){    if(depth[u]<depth[v]) swap(depth[u],depth[v]);    int d=depth[u]-depth[v],tmp=0;    for(int i=0;i<16;i++) if(d&(1<<i)) cal(u,i);//更新路径最大和严格次大值    //for(int i=16;i>=0;i--) if((tmp+=(1<<i))<=d) cal(u);//另一种写法    if(u==v) return;    for(int i=16;i>=0;i--) if(anc[u][i]!=-1&&anc[u][i]!=anc[v][i]) cal(u,i),cal(v,i);    cal2(cost[u]),cal2(cost[v]);}int main(){    //freopen("a.txt","r",stdin);        scanf("%d%d",&n,&m);        for(int i=1;i<=m;i++)            scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].w);        sort(node+1,node+m+1);        kruscal();        dfs(1,-1,0);//转化为有根树        process();//倍增法预处理x的第i个祖先及路径上的最长边和次长边        int ans=INF;        for(int i=1;i<=m;i++){            int u=node[i].u,v=node[i].v,w=node[i].w,tmp;            max1=0,max2=0;            if(!vis[i]){                query(u,v);//求MST上任意两点的最长边和严格次长边                if(max1==w) tmp=mst+w-max2;                else tmp=mst+w-max1;                ans=min(ans,tmp);            }        }        printf("%d\n",ans);}



0 0