poj4006 增大边权求MST

来源:互联网 发布:ubuntu tty 编辑:程序博客网 时间:2024/05/01 03:14

增大边权,求MST,多组询问(只询问,不修改)

做法:1.先求出原图MST,对树中每条边<u,v>,预处理best[u][v]:删除边<u,v>后u所在连通块和v所在连通块通过非树边连接的最短边长

求best[u][v]前还要预处理dp[rt][u]:以rt为根的树,rt到以u为根的子树中所有点通过非树边连接的最短边长

dp[rt][u]和best[u][v]都要通过dfs求出,具体求法见代码


#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>#include<vector>#include<queue>#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 3010const ll mol=1000000007;using namespace std;int n,m,q;ll mst,ma[maxn][maxn],dp[maxn][maxn],best[maxn][maxn],ans;bool vis[maxn][maxn];struct Node{    int from,to;    ll w;    bool operator<(const Node &rhs)const{        return w<rhs.w;    }}node[maxn*maxn];struct Edge{//MST    int to,next;    ll w;}edge[maxn<<2];int head[maxn],tot,p[maxn];void add(int u,int v,ll 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:(p[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,mem(vis,0);    for(int i=1;i<=m;i++){        int x=Find(node[i].from),y=Find(node[i].to);        if(x!=y){            p[x]=y;            vis[node[i].from][node[i].to]=vis[node[i].to][node[i].from]=1;            add(node[i].from,node[i].to,node[i].w);            add(node[i].to,node[i].from,node[i].w);            mst+=node[i].w;        }    }}void dfs1(int u,int fa,int rt){    if(rt!=fa) dp[rt][u]=min(dp[rt][u],ma[u][rt]);    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].to; ll w=edge[i].w;        if(v!=fa){            dfs1(v,u,rt);            dp[rt][u]=min(dp[rt][u],dp[rt][v]);        }    }}ll dfs2(int u,int fa,int rt){    ll ans=dp[u][rt];    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].to;        if(v!=fa){            ans=min(ans,dfs2(v,u,rt));        }    }    return ans;}int main(){    //freopen("a.txt","r",stdin);    while(scanf("%d%d",&n,&m)!=EOF,(m&&n)){        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++){                ma[i][j]=(i==j)?0:INF;                dp[i][j]=best[i][j]=INF;            }        for(int i=1;i<=m;i++){            scanf("%d%d%lld",&node[i].from,&node[i].to,&node[i].w);            node[i].from++,node[i].to++;            ma[node[i].from][node[i].to]=ma[node[i].to][node[i].from]=node[i].w;        }        kruscal();        for(int i=1;i<=n;i++){            dfs1(i,-1,i);//求以i为根的树,i到达树中任意一点为根的子树非树边的最小值        }        for(int u=1;u<=n;u++){            for(int i=head[u];i!=-1;i=edge[i].next){                int v=edge[i].to;                best[u][v]=best[v][u]=dfs2(v,u,u);            }        }        scanf("%d",&q);        int qq=q;        ans=0;        while(qq--){            int a,b;            ll c;            scanf("%d%d%lld",&a,&b,&c);            a++,b++;            if(!vis[a][b]) ans+=mst;            else{                if(best[a][b]>c) ans+=(mst+c-ma[a][b]);                else ans+=(mst+best[a][b]-ma[a][b]);            }        }        double answer=(double)ans/(double)q;        printf("%.4f\n",answer);    }    return 0;}



0 0