【JZOJ 3806】小X 的道路修建

Description

因为一场不小的地震,Y 省n 个城市之间的道路都损坏掉了,省长希望小X 将城市之间的道路重修一遍。
很多城市之间的地基都被地震破坏导致不能修路了,因此可供修建的道路只有m 条。因为施工队伍有限,省长要求用尽量少的道路将所有的城市连通起来,这样施工量就可以尽量少。不过,省长为了表示自己的公正无私,要求在满足上述条件的情况下,选择一种方案,使得该方案中最贵道路的价格和最便宜道路的价格的差值尽量小,即使这样的方案会使总价提升很多也没关系。
小X 现在手忙脚乱,希望你帮帮他。

2 ≤ n ≤ 2000,0 ≤ m ≤ 15000

Solution

发现 O(nm) 可以过耶!
先把边按大小排个序,
对于每条边,扫一遍全局,看看有没有环,有的话把环上最小的边去掉,
每次判断一下是不是联通的,是则更新答案,

复杂度: O(nm+mlog(m))

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
#define min(q,w) ((q)<(w)?(q):(w))
#define max(q,w) ((q)>(w)?(q):(w))
using namespace std;
const int N=2005;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m,ans,FD;
struct qqww
{
    int x,y,v;
}b[N*8];
bool z[N*8];
int B[N*16][5],A[N],B0=1;
bool PX(qqww q,qqww w){return q.v<w.v;}
void DLT(int q)
{
    if(B[q][3])B[B[q][3]][0]=B[q][0];
        else A[B[q^1][1]]=B[q][0];
    B[B[q][0]][3]=B[q][3];
}
void link(int q,int w,int e,int e1)
{
    B[++B0][0]=A[q],B[A[q]][3]=B0,A[q]=B0,B[B0][1]=w,B[B0][2]=e,B[B0][4]=e1;
    B[++B0][0]=A[w],B[A[w]][3]=B0,A[w]=B0,B[B0][1]=q,B[B0][2]=e,B[B0][4]=e1;
}
void dfs(int q,int fa,int zd,int mi)
{
    if(q==zd){FD=mi;return;}
    efo(i,q)if(B[i][1]!=fa)
    {
        if(B[i][2]>B[mi][2])dfs(B[i][1],q,zd,mi);
            else dfs(B[i][1],q,zd,i);
        if(FD)return;
    }
}
int main()
{
    int q,w,e;
    read(n),read(m);
    fo(i,1,m)read(b[i].x),read(b[i].y),read(b[i].v);
    sort(b+1,b+1+m,PX);
    ans=2e9;
    int mi=1,x,y;
    q=0;B[0][2]=1e9;
    fo(i,1,m)
    {
        int x=b[i].x,y=b[i].y;
        FD=0;
        dfs(x,0,y,0);
        if(!FD)q++;
        else 
        {
            z[B[FD][4]]=1;
            DLT(FD),DLT(FD^1);
        }
        link(x,y,b[i].v,i);
        while(z[mi])mi++;
        if(q==n-1)ans=min(ans,b[i].v-b[mi].v);
    }
    if(ans==2e9)printf("-1\n");
        else printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值