题解 P4716 【【模板】最小树形图】

Great_Influence

2018-06-26 16:13:59

Solution

就是模板吧。目前还没找到这个东西除了模板有什么用...... 当然,可以用来解决[这道题](https://www.luogu.org/problemnew/show/P3244)(但是和算法本身没有关系) 以下讲的是朱-刘算法,还有神仙Tarjan提出的更快的算法($O(m+nlogn)$),不过一般用不上就是了。 大体来说,就是发现一个合法的树形图的每个点都只有一条入边~~(废话)~~,所以贪心地对每个点都选择一条通向它的最短的边(没有就没有树形图了)。这样连出来的图有$n-1$条边,但不一定是树。不过,这个图最多只有几个简单环(因为每个点只连入$1$条边)。所以,我们将这些环搜出并缩起来,然后在缩完后的边中继续跑最小树形图直到选择的边构成树。时间复杂度$O(nm)$,但是一般情况会快很多。 注意每次跑一遍算法后,答案已经加上每个点的最短入边了。但是如果要将这条边更换,那么需要将其他的边长减去原来最短入边边长,再继续计算。 然后这道题(以及基本所有的题)只要求这个边权和就可以了,所以就不需要展开之类的操作了。 代码: ```cpp #include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #define Rep(i,a,b) for(register int i=(a),i##end=(b);i<=i##end;++i) #define Repe(i,a,b) for(register int i=(a),i##end=(b);i>=i##end;--i) #define For(i,a,b) for(i=(a),i<=(b);++i) #define Forward(i,a,b) for(i=(a),i>=(b);--i) template<typename T>inline void read(T &x) { T f=1;x=0;char c; for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())x=x*10+(c^48); x*=f; } inline void write(int u,char ed='\n') { if(!u){putchar(48);putchar(ed);return;} static int sta[43],tp; for(tp=0;u;u/=10)sta[++tp]=u%10; for(;tp;putchar(sta[tp--]^48)); putchar(ed); } using namespace std; const int MAXN=111,MAXM=1e4+7; static int n,m,rt; static struct edge { int u,v,w; }p[MAXM]; inline void init() { read(n);read(m);read(rt); Rep(i,1,m)read(p[i].u),read(p[i].v),read(p[i].w); } static int in[MAXN],pre[MAXN],vis[MAXN],id[MAXN]; const int inf=0x3f3f3f3f; inline long long getans() { static long long ans=0; static int cnt=0,u,v,laz; while(1) { Rep(i,1,n)in[i]=inf,id[i]=vis[i]=0; Rep(i,1,m)if(p[i].u^p[i].v&&p[i].w<in[p[i].v]) pre[p[i].v]=p[i].u,in[p[i].v]=p[i].w; in[rt]=0; Rep(i,1,n) { if(in[i]==inf)return -1; ans+=in[i]; for(u=i;u^rt&&vis[u]^i&&!id[u];u=pre[u])vis[u]=i; if(u^rt&&!id[u]) { id[u]=++cnt; for(v=pre[u];v^u;v=pre[v])id[v]=cnt; } } if(!cnt)return ans; Rep(i,1,n)if(!id[i])id[i]=++cnt; Rep(i,1,m) { laz=in[p[i].v]; if((p[i].u=id[p[i].u])^(p[i].v=id[p[i].v])) p[i].w-=laz; } n=cnt;rt=id[rt];cnt=0; } } inline void solve(){printf("%lld\n",getans());} int main() { init(); solve(); return 0; } ```