次小生成树

思想还是很重要的....

首先,次小生成树就是用一条边替换最小生成树中的另一条边. 代价就是两条边的权值差.

 

证明:

我们把"在树上连一条新的边形成一个环,再删掉那个环上一条不是新边的边"的操作叫做边的替换.

替换的代价就是新边权减掉删掉的那条边的边权.

首先,最小生成树通过若干次替换总能够变成次小生成树,这是显然的.

我们把第一次替换的代价记为v1.剩下的所有替换的代价和记为v2.

首先v1和v2均不小于0.否则,对当前的最小生成树应用这些替换,会使总的权值和减小,当前树就不是最小生成树了.

那么如果v2等于0,相当于我们只进行第一次替换,然后什么都不干,于是我们仅通过一次替换得到次小生成树.

如果v2大于0,那么它的权值一定大于只进行第一次替换的权值,此时新树的权值比原树大v1+v2.

而我们肯定有一颗树的权值比原树大v1.因此,我们得到:新树的权值比原树大,还比进行了一次替换的树大.

所以这棵树肯定不是次小生成树.

于是次小生成树一定是仅通过一次在最小生成树上的边替换得到的.

 

 

假设我们用边(i,j) 替换边 (a,b) ,边权函数为 e ,可知 e(i,j)-e(a,b) 应当最小.

我们枚举非树边 (i,j) ,然后计算树上i到j路径上的最长边.

对于常规生成树,显然可以倍增求 LCA(i,j) 以及求路径上的最值.

Kruskal的次小生成树不会TAT

对于Prim,需要开一个数组 f(i,j) 表示最小生成树上i到j的路径中的最长边长度.

维护的时候,如果我们新连了边 (i,j) ,其中 i 不在当前树中, j 在当前树中,那么有递推式

对于每一个当前树上的点k, f(i,k) = f(k,i) = max( f(k,j) , e(i,j) );

然后把 (i,j) 从边表中剔除(或者标记一下),之后我们枚举非树边的时候就方便一些......

为了取得j,还要记录每个节点连到当前树的最短边是哪一个节点的. 维护距离数组的时候顺带更新.

 

AC VIJOS 1070 最小生成树+次小生成树

ExPrim

#include <cstdio>
#include <fstream>
#include <iostream>
 
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
 
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <list>
 
typedef unsigned int uint;
typedef long long int ll;
typedef unsigned long long int ull;
typedef double db;
 
using namespace std;
 
inline int getint()
{
    int res=0;
    char c=getchar();
    bool mi=false;
    while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
    while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
    return mi ? -res : res;
}
inline ll getll()
{
    ll res=0;
    char c=getchar();
    bool mi=false;
    while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
    while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
    return mi ? -res : res;
}

//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================

const ll INF=((ll)1<<56)-1;

int n,m;
ll M[505][505];
ll S[505][505];
ll D[505];
int E[505];
ll F[505][505];
bool used[505];


void putres(ll x)
{ printf("Cost: %I64d\n",x); }

int main()
{
    n=getll();
    m=getll();
    
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    M[i][j]=S[i][j]=INF;
    
    for(int i=0;i<m;i++)
    {
        int a=getint()-1;
        int b=getint()-1;
        ll v=getint();
        if(v<M[a][b])
        {
            S[a][b]=S[b][a]=M[a][b];
            M[a][b]=M[b][a]=v;
        }
        else if(v<S[a][b])
            S[a][b]=S[b][a]=v;
    }
    
    //Prim
    for(int i=1;i<n;i++) D[i]=INF;
    ll res=0;
    int d;
    for(d=0;d<n;d++)
    {
        int x=-1;
        ll mi=INF;
        for(int i=0;i<n;i++)
        if(!used[i] && mi>D[i])
        { mi=D[i]; x=i; }
        
        if(mi==INF) break; //unable to find any edge.
        used[x]=true;
        res+=mi;
        
        F[E[x]][x]=F[x][E[x]]=mi;
        for(int i=0;i<n;i++)
        if(used[i] && i!=x && i!=E[x])
        F[i][x]=F[x][i]=max(F[i][E[x]],mi);
        
        M[E[x]][x]=M[x][E[x]]=S[x][E[x]];
        
        for(int i=0;i<n;i++)
        if(!used[i] && D[i]>M[i][x])
        { D[i]=M[i][x]; E[i]=x; }
    }
    
    if(d!=n) { putres(-1),putres(-1); return 0; }
    
    bool tag=true;
    for(int i=0;i<n && tag;i++)
    for(int j=0;j<n && tag;j++)
    if(M[i][j]!=INF) tag=false;
    
    if(tag) { putres(res),putres(-1); return 0; }
    
    ll delta=INF;
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    if(M[i][j]!=INF)
    delta=min(delta,M[i][j]-F[i][j]);
    
    putres(res),putres(delta+res);
    
    return 0;
}
View Code

ExKruskal

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <iostream>
  4  
  5 #include <cstdlib>
  6 #include <cstring>
  7 #include <algorithm>
  8 #include <cmath>
  9  
 10 #include <queue>
 11 #include <vector>
 12 #include <map>
 13 #include <set>
 14 #include <stack>
 15 #include <list>
 16  
 17 typedef unsigned int uint;
 18 typedef long long int ll;
 19 typedef unsigned long long int ull;
 20 typedef double db;
 21  
 22 using namespace std;
 23  
 24 inline int getint()
 25 {
 26     int res=0;
 27     char c=getchar();
 28     bool mi=false;
 29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 31     return mi ? -res : res;
 32 }
 33 inline ll getll()
 34 {
 35     ll res=0;
 36     char c=getchar();
 37     bool mi=false;
 38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 40     return mi ? -res : res;
 41 }
 42 
 43 //==============================================================================
 44 //==============================================================================
 45 //==============================================================================
 46 //==============================================================================
 47 
 48 const ll INF=(ll(1)<<56)-1;
 49 
 50 int n,m;
 51 
 52 struct vedge
 53 {
 54     int a,b;
 55     ll v;
 56     bool used;
 57 }E[200000];
 58 int p[200000];
 59 bool cmp(int x,int y)
 60 { return E[x].v<E[y].v; }
 61 
 62 //union find
 63 int ft[1000];
 64 void INIT(int size)
 65 { for(int i=0;i<=size;i++) ft[i]=i; }
 66 int findf(int x)
 67 { return ft[x]==x ? x : ft[x]=findf(ft[x]); }
 68 void con(int a,int b)
 69 { ft[findf(a)]=findf(b); }
 70 
 71 struct edge
 72 {
 73     int in;
 74     ll v;
 75     edge*nxt;
 76 }pool[10000];
 77 edge*et=pool;
 78 edge*eds[1000];
 79 void addedge(int i,int j,ll v)
 80 {
 81     et->in=j; et->v=v; et->nxt=eds[i]; eds[i]=et++;
 82     et->in=i; et->v=v; et->nxt=eds[j]; eds[j]=et++;
 83 }
 84 #define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt)
 85 
 86 //DFS
 87 int f[11][1050];
 88 ll mx[11][1050];
 89 int dep[1050];
 90 ll v[1050];
 91 int curd;
 92 void DFS(int x,ll k)
 93 {
 94     dep[x]=curd;
 95     v[x]=k;
 96     mx[0][x]=max(v[x],v[f[0][x]]);
 97     
 98     curd++;
 99     FOREACH_EDGE(i,x)
100     if(i->in!=f[0][x])
101     {
102         f[0][i->in]=x;
103         DFS(i->in,i->v);
104     }
105     curd--;
106 }
107 
108 void gen()
109 {
110     for(int d=1;d<11;d++)
111     {
112         for(int i=0;i<n;i++)
113         {
114             f[d][i]=f[d-1][f[d-1][i]];
115             mx[d][i]=max(mx[d-1][i],mx[d-1][f[d-1][i]]);
116         }
117     }
118 }
119 
120 int getLCA(int a,int b)
121 {
122     if(dep[a]<dep[b]) swap(a,b);
123     
124     for(int i=10;i>=0;i--)
125     if(dep[f[i][a]]>=dep[b]) a=f[i][a];
126     
127     if(a==b) return b;
128 
129     for(int i=10;i>=0;i--)
130     if(f[i][a]!=f[i][b])
131     a=f[i][a],b=f[i][b];
132     
133     return f[0][a];
134 }
135 
136 ll getv(int a,int b)
137 {
138     if(dep[a]<dep[b]) swap(a,b);
139     
140     ll res=-INF;
141     
142     int lca=getLCA(a,b);
143     
144     if(lca==a) res=v[b];
145     else if(lca==b) res=v[a];
146     else res=max(v[a],v[b]);
147     
148     for(int i=10;i>=0;i--)
149     if(dep[f[i][a]]>dep[lca])
150     res=max(res,mx[i][a]),a=f[i][a];
151 
152     for(int i=10;i>=0;i--)
153     if(dep[f[i][b]]>dep[lca])
154     res=max(res,mx[i][b]),b=f[i][b];
155     
156     return res;
157 }
158 
159 
160 int main()
161 {
162     
163     freopen("in.txt","r",stdin);
164     freopen("out.txt","w",stdout);
165     
166     n=getint();
167     m=getint();
168     for(int i=0;i<m;i++)
169     E[i].a=getint()-1,
170     E[i].b=getint()-1,
171     E[i].v=getll();
172     
173     //Kruskal
174     for(int i=0;i<m;i++) p[i]=i;
175     stable_sort(p,p+m,cmp);
176     INIT(n+1);
177     ll cres=0;
178     int tot=0;
179     for(int i=0;i<m;i++)
180     if(findf(E[p[i]].a)!=findf(E[p[i]].b))
181     {
182         con(E[p[i]].a,E[p[i]].b);
183         addedge(E[p[i]].a,E[p[i]].b,E[p[i]].v);
184         E[p[i]].used=true;
185         cres+=E[p[i]].v;
186         tot++;
187     }
188     
189     ll res=INF;
190     if(tot!=n-1) cres=-1;
191     else
192     {
193         //second generated tree
194         f[0][0]=0;
195         curd=0;
196         DFS(0,0);
197         gen();
198         
199         for(int i=0;i<m;i++)
200         if(!E[i].used && E[i].a!=E[i].b)
201         {
202             ll rk=getv(E[i].a,E[i].b);
203             res=min(res,cres-rk+E[i].v);
204         }
205     }
206     
207     if(res>=INF/10) res=-1;
208     
209     printf("Cost: %I64d\nCost: %I64d\n",cres,res);
210     
211     return 0;
212 }
View Code

很久很久以前写的,但是一开始WA了.....错在哪呢.....

变量名打错,n打成m! 倍增的时候dep[lca]写成lca! shenmegui.........

最终A了.....不是很快的样子.....

 

 

..

posted @ 2015-06-04 11:25  DragoonKiller  阅读(164)  评论(0编辑  收藏  举报