1. 程式人生 > >bzoj3624:[Apio2008]免費道路

bzoj3624:[Apio2008]免費道路

i++ efi 情況 www oid names printf ace pac

傳送門

顯然是求生成樹
只有兩種情況會導致no solution:
1、如何加邊圖都不連通
2、必須要加的鵝卵石邊超過k條
首先可以一遍kruskal判斷出必須要加的鵝卵石邊是多少:優先加水泥路的邊
然後將必須要加的鵝卵石邊加上去,再多加幾條邊使鵝卵石邊等於k條,再去加水泥路
最後判斷圖是否聯通就行了(也就是加的邊數是否是n-1條)

現在考慮正確性:由於一開始處理出了必須要加的鵝卵石邊,所以其他的鵝卵石邊和水泥路邊都是可以互相替換的,這樣就可以保證剩下的邊可以隨便加了
代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e5+10;
int n,m,k,tot,f[maxn],ans,cnt,h[maxn];
struct oo{int x,y,z;}a[maxn];bool vis[maxn];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
bool cmp(oo a,oo b){return a.z>b.z;}
int main()
{
    read(n),read(m),read(k);
    for(rg int i=1;i<=n;i++)f[i]=i;
    for(rg int i=1;i<=m;i++)read(a[i].x),read(a[i].y),read(a[i].z);
    sort(a+1,a+m+1,cmp);
    for(rg int i=1;i<=m;i++)
        if(find(a[i].x)!=find(a[i].y))
        {
            int x=find(a[i].x),y=find(a[i].y);f[x]=y;
            if(!a[i].z)ans++,vis[i]=1;
        }
    if(ans>k){printf("no solution\n");return 0;}
    for(rg int i=1;i<=n;i++)f[i]=i;
    for(rg int i=m;i;i--)
        if(vis[i]){int x=find(a[i].x),y=find(a[i].y);f[x]=y;}
    for(rg int i=m;i;i--)
        if(find(a[i].x)!=find(a[i].y))
        {
            int x=find(a[i].x),y=find(a[i].y);f[x]=y;
            if(!a[i].z)ans++,vis[i]=1;
            if(ans==k)break;
        }
    for(rg int i=1;i<=m;i++)
        if(find(a[i].x)!=find(a[i].y))
        {
            int x=find(a[i].x),y=find(a[i].y);f[x]=y;
            vis[i]=1;if(!a[i].z)break; 
        }
    for(rg int i=1;i<=m;i++)if(vis[i])tot++;
    if(tot<n-1){printf("no solution\n");return 0;}
    for(rg int i=1;i<=m;i++)if(vis[i])printf("%d %d %d\n",a[i].x,a[i].y,a[i].z);
}

bzoj3624:[Apio2008]免費道路