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

/*
题意: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;
}


 

你可能感兴趣的:(大连赛区网赛1005 对方加一条边,我方减一条边最少的费用)