您的位置:首页 > 其它

大连赛区网赛1005 对方加一条边,我方减一条边最少的费用

2011-09-03 17:15 330 查看
/*
题意:N个点,M条无向有权边,求一个数,使得加任何一条边后,可以破坏一条小于或等于这个数的边,使得不连通
分析:先缩点形成树。而后,加上的边一定会包含最小的边权。所以分别以这条边的两端点建树。记录以某个点为根结点
的最小边权值,然后再搜一遍,确定方向。
*/
#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;

const __int64 maxn=11000;
const __int64 maxm=210000;
const __int64 maxint=110000000;
struct edge
{
__int64 u,v,w,next;
}e[maxm],e1[maxm];
__int64 edgeNum,top,tnum,num,first[maxn],low[maxn],DFN[maxn],belong[maxn],q[maxn];
__int64 in[maxn],lmin[maxn];
__int64 enum1,first1[maxn],sum,f,f1,f2;
bool inq[maxn];

void Addedge(__int64 u,__int64 v,__int64 w,__int64 first[],__int64 &edgeNum,edge e[])
{
e[edgeNum].u=u,e[edgeNum].v=v,e[edgeNum].w=w,e[edgeNum].next=first[u],first[u]=edgeNum++;
e[edgeNum].u=v,e[edgeNum].v=u,e[edgeNum].w=w,e[edgeNum].next=first[v],first[v]=edgeNum++;
}

void Tarjan(__int64 t,__int64 f)
{
low[t]=DFN[t]=++tnum;
q[top++]=t;
inq[t]=true;

__int64 i,k;
for(k=first[t];k!=-1;k=e[k].next)
{
i=e[k].v;
if(i==f) continue;
if(!DFN[i])
{
Tarjan(i,t);
if(low[t]>low[i])
low[t]=low[i];
}
else if(inq[i]&&low[t]>DFN[i])
low[t]=DFN[i];
}

if(low[t]==DFN[t])
{
++num;
do
{
inq[q[--top]]=false;
belong[q[top]]=num;
}while(q[top]!=t);
}
}
__int64 Min(__int64 a,__int64 b)
{
return a<b? a:b;
}
void DFS(__int64 t,__int64 p)
{
__int64 k,j,jj,ii=-1,kk=maxint;
for(k=first1[t];k!=-1;k=e1[k].next)
{
j=e1[k].v;
if(j==f1||j==f2||j==p) continue;
DFS(j,t);
jj=Min(lmin[j],e1[k].w);//子代或这条边的较小值为最小的边
if(lmin[t]>jj)
lmin[t]=jj;

if(jj<kk)
{
kk=jj;
ii=j;//记录最小的边的走向
}
}
for(k=first1[t];k!=-1;k=e1[k].next)
{
j=e1[k].v;
if(j==f1||j==f2||j==p||j==ii) continue;//不是含最小的边的子树
jj=Min(lmin[j],e1[k].w);
if(jj<sum)
sum=jj;
}
if(ii!=-1)//走向最小的边的子树
DFS(ii,t);
}

int main()
{
__int64 n,m,i,j,k,u,v,w;
while(scanf("%I64d%I64d",&n,&m)!=EOF)
{
memset(first,-1,sizeof(first));
for(edgeNum=0,i=0;i<m;i++)
{
scanf("%I64d%I64d%I64d",&u,&v,&w);
Addedge(u,v,w,first,edgeNum,e);
}

//Tarjan算法算边连通分量
memset(DFN,0,sizeof(DFN));
memset(inq,false,sizeof(inq));
memset(belong,0,sizeof(belong));
for(top=0,tnum=0,num=0,i=1;i<=n;i++)
if(!DFN[i])
Tarjan(i,-1);

//根据边连通分量缩点建树,并确定树的根结点
memset(in,0,sizeof(in));
memset(first1,-1,sizeof(first1));
for(enum1=0,j=100000000,k=-1,i=0;i<m;i++)
if(belong[e[2*i].u]!=belong[e[2*i].v])
{
Addedge(belong[e[2*i].u],belong[e[2*i].v],e[2*i].w,first1,enum1,e1);

in[belong[e[2*i].u]]++;
in[belong[e[2*i].v]]++;
if(e[2*i].w<j)
{
j=e[2*i].w;
k=2*i;
}
}

for(j=0,i=1;i<=num;i++)
{
if(in[i]==0)//注意数据有可能不连通,不加这条就会TLE
break;
if(in[i]<=1) j++;
}
if(i<=num||j<=2)//叶子结点小于或等于2个
{
printf("-1\n");
continue;
}

for(i=1;i<=num;i++)//以i为根结点的子树中最小的边权值
lmin[i]=maxint;
f1=belong[e[k].u];
f2=belong[e[k].v];
//分别以f1,f2为根结点,找第二小的路径
sum=maxint;
DFS(f1,-1);
DFS(f2,-1);
if(sum==maxint) printf("-1\n");
else printf("%I64d\n",sum);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: