【[APIO2008]免費道路】
阿新 • • 發佈:2019-01-01
\(kruskal\)好題
\(0\)邊的數量在某些情況下是可以無限制的調控的,前提是所有必須存在的邊都在生成樹裡了
所以應該分別求出有哪些邊是必須在生成樹裡的,我們可以先從大到小排序,求出有哪些\(0\)邊必須在生成樹裡,之後再從小到大排序,求出那些\(1\)邊必須在生成樹裡
之後剩下的邊就可以隨便放了,調控\(0\)邊的個數恰好為\(k\)即可
程式碼
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #define LL long long #define re register #define maxn 20005 struct E { int u,v,w; }e[100005],Ans[100005]; inline int read() { char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } int fa[maxn],sz[maxn]; int n,m,tot,k,num; inline void Rebuild() { for(re int i=1;i<=n;i++) fa[i]=i,sz[i]=1; } int find(int x) { if(x==fa[x]) return x; return fa[x]=find(fa[x]); } inline int merge(int x,int y) { int xx=find(x),yy=find(y); if(xx==yy) return 0; if(sz[xx]>sz[yy]) fa[yy]=xx,sz[xx]+=sz[yy]; else fa[xx]=yy,sz[yy]+=sz[xx]; return 1; } inline int cmp1(E A,E B) { return A.w<B.w; } inline int cmp2(E A,E B) { return A.w>B.w; } int main() { n=read(),m=read(),k=read(); for(re int i=1;i<=m;i++) e[i].u=read(),e[i].v=read(),e[i].w=read(); Rebuild(); std::sort(e+1,e+m+1,cmp2); for(re int i=1;i<=m;i++) if(merge(e[i].u,e[i].v)&&!e[i].w) Ans[++tot].u=e[i].u,Ans[tot].v=e[i].v,Ans[tot].w=0,num++; if(tot>k) { puts("no solution"); return 0; } std::sort(e+1,e+m+1,cmp1); Rebuild(); for(re int i=1;i<=m;i++) if(merge(e[i].u,e[i].v)&&e[i].w) Ans[++tot].u=e[i].u,Ans[tot].v=e[i].v,Ans[tot].w=1; Rebuild(); for(re int i=1;i<=tot;i++) merge(Ans[i].u,Ans[i].v); for(re int i=1;i<=m;i++) { if(!e[i].w&&num>=k) continue; if(merge(e[i].u,e[i].v)) { if(!e[i].w&&num<k) num++; Ans[++tot].u=e[i].u,Ans[tot].v=e[i].v,Ans[tot].w=e[i].w; } } if(tot!=n-1||num<k) puts("no solution"); else for(re int i=1;i<=tot;i++) printf("%d %d %d\n",Ans[i].u,Ans[i].v,Ans[i].w); return 0; }