【BZOJ1977】[BeiJing2010组队]次小生成树 Tree
Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
HINT
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
题解:结论!次小生成树一定可以由 最小生成树+一条非树边-这条非树边覆盖的一条边 得到。证明方法。。。假设我们要加入两条边,先加一条边a使总权值变大,再加一条边b使权值变小。那么b所替换掉的边一定在a形成的环上,因为a是非树边,所以a的权值比环上的所有边都大,所以b替换掉的边权值一定不会大于a,所以我们就白加入a这条边了。(奇怪的证明)
所以我们要替换那条边呢?如果它所覆盖的边的最大值=这条非树边的权值,则替换它所覆盖的边的次大值;如果不相等,则直接替换最大值。这就需要我们求出一条路径上的权值最大值和次大值,可以用树剖,当然我觉得倍增更优美一点~
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int maxn=100010; int n,m,cnt; ll ans,sum; struct edge { int a,b,val,used; }p[maxn*3]; struct node { int x,y; node() {x=0,y=-1;} node(int _){x=_,y=0;} node operator + (const node &a)const { node b; if(x<a.x) b.x=a.x,b.y=max(x,a.y); if(x>a.x) b.x=x,b.y=max(y,a.x); if(x==a.x) b.x=x,b.y=max(y,a.y); return b; } }s[19][maxn]; int to[maxn<<1],next[maxn<<1],head[maxn],val[maxn<<1],fa[19][maxn],dep[maxn],Log[maxn],f[maxn]; bool cmp(const edge &a,const edge &b) { return a.val<b.val; } int find(int x) { return (f[x]==x)?x:f[x]=find(f[x]); } inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } inline node ask(int a,int b) { if(dep[a]<dep[b]) swap(a,b); node ret; for(int i=Log[dep[a]-dep[b]];i>=0;i--) if(dep[fa[i][a]]>=dep[b]) ret=ret+s[i][a],a=fa[i][a]; if(a==b) return ret; for(int i=Log[dep[a]];i>=0;i--) if(fa[i][a]!=fa[i][b]) ret=ret+s[i][a]+s[i][b],a=fa[i][a],b=fa[i][b]; return ret+s[0][a]+s[0][b]; } void dfs(int x) { for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[0][x]) fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,s[0][to[i]]=node(val[i]),dfs(to[i]); } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i,j,a,b; for(i=1;i<=m;i++) p[i].a=rd(),p[i].b=rd(),p[i].val=rd(); sort(p+1,p+m+1,cmp); for(i=1;i<=n;i++) f[i]=i; memset(head,-1,sizeof(head)); for(i=1;i<=m;i++) { a=find(p[i].a),b=find(p[i].b); if(a!=b) { add(p[i].a,p[i].b,p[i].val),add(p[i].b,p[i].a,p[i].val),f[a]=b,p[i].used=1,sum+=p[i].val; if(cnt==(n-1)<<1) break; } } dep[1]=1,dfs(1); for(i=2;i<=n;i++) Log[i]=Log[i>>1]+1; for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]],s[j][i]=s[j-1][i]+s[j-1][fa[j-1][i]]; ans=1ll<<60; for(i=1;i<=m;i++) if(!p[i].used) { node tmp=ask(p[i].a,p[i].b); if(tmp.x!=p[i].val&&tmp.x>0) ans=min(ans,sum+p[i].val-tmp.x); if(tmp.y>0) ans=min(ans,sum+p[i].val-tmp.y); } printf("%lld",ans); return 0; }