APIO2013 道路費用【最小生成樹】
阿新 • • 發佈:2020-11-26
題目解析
根據\(k\)的範圍,不難想到我們\(2^k\)列舉所有新邊是否在\(MST\)中,然後再加入原始邊,計算出答案取最值。
但是這樣做複雜度過不去。
先考慮把\(k\)條新邊加進去,然後再按照\(Kruskal\)演算法加入\(n-1-k\)條原始邊,形成一棵樹。由於原始邊的權值各不相同,那麼目前加入的這些原始邊集合是唯一確定的。
又因為在\(MST\)裡的新邊條數最多為\(k\),那麼加入的原始邊數量不會比現在更少,又是從小到大加邊,目前已經加入的原始邊一定一直在\(MST\)中。
所以我們可以把這些固定的原始邊連在一起,進行縮點。
那麼現在圖中只剩下\(k+1\)個結點了(我們還差\(k\)
誒嘿,然後我們現在發現剛才的那個做法,它又可以了,所以那麼做就行了。
►Code View
#include<cstdio> #include<algorithm> #include<vector> #include<queue> #include<cstring> using namespace std; #define N 100005 #define M 200005 #define INF 0x3f3f3f3f #define LL long long int rd() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f*x; } struct node{ int u,v,w; }e[M],g[25],e2[M]; int cnt; bool cmp(node p,node q) { return p.w<q.w; } int n,m,k; int p[N],pe[N]; int id[N],tot; int rt; struct un{ int f[N]; void Init() { for(int i=1;i<=n;i++) f[i]=i,f1[i]=i; } int Find(int x) { if(f[x]==x) return x; return f[x]=Find(f[x]); } bool Union(int u,int v) { u=Find(u),v=Find(v); if(u==v) return 0; if(u<v) f[u]=v; else f[v]=u; return 1; } }A,B; int main() { n=rd(),m=rd(),k=rd(); A.Init(); B.Init(); for(int i=1;i<=m;i++) e[i].u=rd(),e[i].v=rd(),e[i].w=rd(); for(int i=1;i<=k;i++) { g[i].u=rd(),g[i].v=rd(); A.Union(g[i].u,g[i].v); } for(int i=1;i<=n;i++) p[i]=rd(); sort(e+1,e+m+1,cmp); int num=k; for(int i=1;i<=m;i++) { if(A.Union(e[i].u,e[i].v)) { num++; B.Union(e[i].u,e[i].v); } if(num==n-1) break; } for(int i=1;i<=n;i++) if(B.Find(i)==i) id[i]=++tot;//縮點 for(int i=1;i<=n;i++) pe[id[B.Find(i)]]+=p[i];//縮點 rt=id[B.Find(1)]; A=B; for(int i=1;i<=m;i++) if(B.Union(e[i].u,e[i].v)) e2[++cnt]=e[i];//可能加入的原始邊 for(int i=1;i<=k;i++) g[i].u=id[A.Find(g[i].u)],g[i].v=id[A.Find(g[i].v)];//縮點 for(int i=1;i<=cnt;i++) e2[i].u=id[A.Find(e2[i].u)],e2[i].v=id[A.Find(e2[i].v)];//縮點 for(int S=0;S<(1<<k);S++) { } return 0; }