zoukankan      html  css  js  c++  java
  • UVA

       一道特别好的题qwq。

        题目大意就是给你一个无向图,让你把边定向之后再加一些边使得这个图强连通,求最少需要加多少边。

        一开始毫无头绪23333,数据范围让人摸不着头脑。。。。。

        然后开始画图,,,发现环上的边都顺时针或者逆时针是很优的,之后扩展到了 边双联通分量上(因为边双联通分量中的每个边都在至少一个环中嘛):可以发现每个边双联通分量都存在一种边的定向方式,使得定向之后这个联通分量是强连通分量。

        这个考虑一个个把环缩起来就行了吧。

        

        于是我们先跑一遍 tarjan,把图中所有桥找出来,然后图中的边双就可以缩成一个点啦,此时这个图就缩成了一个森林,于是现在的问题就变成了:给你一个森林,求最少添加多少条边,使得图中不再存在割边(也就是合并所有边双)。

        

        这时我猛然想起之前做过的一道题: http://www.cnblogs.com/JYYHH/p/8418923.html,有没有感觉很相似,只不过这个题不是树而是森林。。。。。

        至于为什么一个树的最优方案是 (叶子数+1)/2 这里就不再解释了,那篇博客已经讲了。

        然后我们需要把这个结论扩展成 => 一个森林的最优方案是 (叶子数 + 孤立顶点数*2 +1)/2.

        考虑把所有树合并成一颗大树,其中连的边必须保证 两端都是 叶子或者孤立顶点,这样一条边会带来1的代价,并且使  叶子数 + 孤立顶点数*2 +1  这个值减少2。因为这样并不影响 叶子数 + 孤立顶点数*2 +1 的奇偶性,所以最后合并成一颗树之后再用之前的那个结论就行了。

    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    using namespace std;
    const int maxn=1005;
    int to[maxn*1000],ne[maxn*1000],num,dc,uu,vv,K,ans;
    int hd[maxn],n,m,col[maxn],dfn[maxn],low[maxn],D[maxn];
    bool ban[maxn*1000];
    inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;}
    
    inline void init(){
        fill(ban,ban+num+1,0),num=1;
    	memset(hd,0,sizeof(hd)),ans=0;
    	memset(col,0,sizeof(col));
    	memset(dfn,0,sizeof(dfn));
    	memset(D,0,sizeof(D));
    }
    
    void dfs(int x,int fa){
    	dfn[x]=low[x]=++dc;
    	for(int i=hd[x];i;i=ne[i]){
    		if(to[i]==fa) continue;
    	    if(!dfn[to[i]]){
    	    	dfs(to[i],x),low[x]=min(low[x],low[to[i]]);
    	    	if(low[to[i]]>dfn[x]) ban[i]=ban[i^1]=1;
    		}
    		else low[x]=min(low[x],dfn[to[i]]);
    	}
    }
    
    void Search(int x){
    	col[x]=K;
    	for(int i=hd[x];i;i=ne[i]) if(!ban[i]&&!col[to[i]]) Search(to[i]);
    }
    
    inline void calc(){
    	for(int i=0;i<=num;i++) if(ban[i]) D[col[to[i]]]++;
    	for(int i=1;i<=K;i++) if(D[i]<2) ans+=2-D[i];
    }
    
    int main(){
    	while(scanf("%d%d",&n,&m)==2){
    		init(),dc=K=0;
    		for(int i=1;i<=m;i++) scanf("%d%d",&uu,&vv),add(uu,vv),add(vv,uu);
    		for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i,-1);
    		for(int i=1;i<=n;i++) if(!col[i]) K++,Search(i);
    		if(K==1) puts("0");
    		else calc(),printf("%d
    ",(ans+1)>>1);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    ARM处理器
    进程和线程通俗理解
    const与指针
    字符提取命令
    ThinkPHP之视图模版的使用
    ThinkPHP之MVC与URL访问
    ThinkPHP之项目搭建
    android之文件存储和读取
    cryptdb中wrapper.lua的分析
    cryptDB安装分析
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8909433.html
Copyright © 2011-2022 走看看