2013 多校第四场 hdu 4635 Strongly connected

hdu 4635

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4635

题目大意:给你一个简单图,点数为n,边数为m,边是单向变,问你最多能添多少边使他变成一个强连通图。

思路:先缩点是肯定的,然后对于缩点后的图,搞出两个点集,一个为入度为0或者出度为0的包含点数最小的强连通分量,其中点数记为a,则另一个为b = n-a,那么答案就是ans = a*(a-1)+b*(b-1)+a*b-m,(a*( a-1) 是把有a个点的点集搞成饱和,需要的所有边,两个加起来,就是两个点集各自搞成饱和的边,a*b是两个点集搞起来但是不强连通,加的最多的边,即边都往一个方向,最后-m,就是把原来的边剪掉)。至于为什么是找点数最小的入度为0或者出度为0的,是因为如果你设个未知量去算,就会发现其实他是个二次函数,对称轴为n/2且取值最小,那么当然要是两边比较大啦。而入度为0或者出度为0,是因为如果同时又入度和出度,那么就不可能把除它以外的点全搞起来,因为马上就会强连通,画一下就知道了。

比赛的时候,这道题出的那么多,就代表它不是什么难题,我们先想到缩点,缩点之后有想到数,然后又去考虑子树什么去了,哎,想复杂了,后面应该是找最大和最小,我们当时也想成找最接近n/2的两个数去了,因为光想成a*b最大了,漏考虑了两个点集本身还要先自己搞好,哎呀,这么简单的图论题,反正最后是没搞出来。。T T

代码如下:

#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
using namespace std;

const int MAXN = 111111 ;

int n,m;

struct Edge
{
    int t,next;
} edge[MAXN];

int tot,head[MAXN];

void add_edge(int s,int t)
{
    edge[tot].t = t;
    edge[tot].next = head[s];
    head[s] = tot++;
}

stack <int> s;
int dfs_clock,pre[MAXN],scc_cnt,low_link[MAXN],sccno[MAXN];

void dfs(int u)
{
	low_link[u]=pre[u]=++dfs_clock;
	s.push(u);
	for(int i=head[u];i!=-1;i = edge[i].next)
	{
		int v=edge[i].t;
		if(!pre[v])
		{
			dfs(v);
			low_link[u]=min(low_link[u],low_link[v]);
		}
		else if(!sccno[v])
			low_link[u]=min(low_link[u],pre[v]);
	}
	if(pre[u]==low_link[u])
	{
		scc_cnt++;
		while(!s.empty())
		{
			int x=s.top();
			s.pop();
			sccno[x]=scc_cnt;
			if(x==u) break;
		}
	}
}

void tarjan()
{
	scc_cnt=0;
	dfs_clock=0;
	memset(pre,0,sizeof(pre));
	memset(sccno,0,sizeof(sccno));
	for(int i=1;i<=n;i++)
		if(!sccno[i])
			dfs(i);
}

int ans;

int in[MAXN],out[MAXN],num[MAXN];

void find()
{
    if(scc_cnt==1)
    {
        ans = -1;
        return ;
    }
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(num,0,sizeof(num));
    for(int i = 1 ;i<=n;i++)
    {
        num[sccno[i]]++;
        for(int e = head[i] ; e!=-1; e =edge[e].next)
        {
            int v = edge[e].t;
            if(sccno[i]!=sccno[v])
            {
                in[sccno[v]]++;
                out[sccno[i]]++;
            }
        }
    }
    int minn = n+1;
    for(int i = 1;i<=scc_cnt;i++)
        if(in[i]==0||out[i]==0)
        {
            if(num[i]<minn)
            {
                minn = num[i];
            }
        }
    int a = minn,b = n-minn;
    ans = a*(a-1)+b*(b-1)+a*b-m;
}

int main()
{
    int T;
    scanf("%d",&T);
    for(int cas = 1;cas<=T;cas++)
    {
        scanf("%d%d",&n,&m);
        int a,b;
        tot=0;
        memset(head,-1,sizeof(head));
        for(int i = 0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            add_edge(a,b);
        }
        tarjan();
        find();
        printf("Case %d: %d\n",cas,ans);
    }
    return 0;
}


你可能感兴趣的:(2013 多校第四场 hdu 4635 Strongly connected)