HDOJ2242解题报告【边双连通分量】

题目地址:

  http://acm.hdu.edu.cn/showproblem.php?pid=2242

题目概述:

  中文题面就不赘述了。

大致思路:

  其实读完题之后就知道是要求这张图所有的桥,并且分别删掉这些桥来更新答案。

  那么就是求边双联通分量了,求出来之后缩点,原图变成一棵树,然后在树上维护这个点的子树的权值和,然后枚举树上所有点来更新答案即可,详见代码。

复杂度分析:

  略。(才不是因为不会分析)

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <ctime>
#include <map>
#include <assert.h>
#include <stack>
#include <set>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

#define sacnf scanf
#define scnaf scanf
#define pb push_back
#define Len size()
#define maxn 10010
#define maxm 20010
#define inf 1061109567
//const long long inf=1e15;
#define INF 0x3f3f3f3f
#define Eps 0.000001
const double PI=acos(-1.0);
#define mod 1000000007
#define MAXNUM 10000
#define For(i,j,k) for(int (i)=(j);(i)<=(k);(i)++)
#define mes(a,b) memset((a),(b),sizeof(a))
#define scanfi(a) scanf("%d",&(a))
typedef long long ll;
typedef unsigned long long ulld;
void Swap(int &a,int &b) {int t=a;a=b;b=t;}
ll Abs(ll x) {return (x<0)?-x:x;}

struct Edge
{
    int from, to, next;
} edge[maxm];

int head[maxn],edgenum;
int low[maxn], dfn[maxn],dfs_clock;
int ebcno[maxn], ebc_cnt;
stack<int> S;
bool Instack[maxn];
vector<int> ebc[maxn],G[maxn];
int total,ans;
int num1[maxn],num2[maxn],d[maxn];

void addEdge(int u,int v)
{
    edge[edgenum].from=u;edge[edgenum].to=v;
    edge[edgenum].next=head[u];head[u]=edgenum++;
}

void tarjan(int u,int fa)
{
    int flag=0;
    low[u]=dfn[u]=++dfs_clock;
    S.push(u);Instack[u]=true;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa&&!flag) {flag=1;continue;}
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
        }
        else if(Instack[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        ebc_cnt++;
        ebc[ebc_cnt].clear();
        for(;;)
        {
            int v=S.top();S.pop();Instack[v]=false;
            ebcno[v]=ebc_cnt;ebc[ebc_cnt].push_back(v);
            if(v==u) break;
        }
    }
}

void find_cut(int l, int r)
{
    dfs_clock=ebc_cnt=0;
    mes(low,0);mes(dfn,0);mes(ebcno,0);
    mes(Instack,false);
    For(i,l,r)
        if(!dfn[i]) tarjan(i,-1);
}

void dfs(int u, int fa)
{
     d[u]=num2[u];int len=G[u].Len;
     For(i,0,len-1)
     {
         int v=G[u][i];
         if(v==fa) continue;
         dfs(v,u);d[u]+=d[v];
     }
     ans=min(ans,abs(total-2*d[u]));
}

int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    //clock_t st=clock();
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        mes(head,-1);edgenum=0;total=0;
        For(i,1,n) scanfi(num1[i]),total+=num1[i];
        int a,b;
        For(i,1,m)
        {
            scanf("%d%d", &a,&b);a++;b++;
            addEdge(a,b);addEdge(b,a);
        }
        find_cut(1,n);
        if(ebc_cnt==1)
        {
            printf("impossible\n");
            continue;
        }
        For(i,1,ebc_cnt)
        {
            G[i].clear();int sum=0,len=ebc[i].Len;
            For(j,0,len-1) sum+=num1[ebc[i][j]];
            num2[i]=sum;
        }
        for(int i=0;i<edgenum;i+=2)
        {
            int u=ebcno[edge[i].from];
            int v=ebcno[edge[i].to];
            if(u!=v)
                G[u].push_back(v),G[v].push_back(u);
        }
        ans=inf;dfs(1,-1);
        printf("%d\n",ans);
    }
    //clock_t ed=clock();
    //printf("\n\nTime Used : %.5lf Ms.\n",(double)(ed-st)/CLOCKS_PER_SEC);
    return 0;
}
时间: 2024-10-10 18:18:01

HDOJ2242解题报告【边双连通分量】的相关文章

BZOJ2730 矿场搭建 解题报告 点双联通分量

题意概述: 一张有向图,在其中设置一些关键点(即题目中的逃生出口),使得删除任意一个点之后其余点都可以到达至少一个关键点. 问至少需要设置多少中关键点,有多少种设置方法. 解析: 首先,这道题要求删掉一个点,不难想到这道题与割点有关.其次,删掉一个点其他点仍然可以到达关键点就可以想到是点双联通分量. 但是,问题关键是,真的需要在每一个点双联通分量中都设置一个关键点吗? 答案是否定的,因为如果一个双联通分量连接了两个或两个以上的割点,一个割点被删掉那么还可以通过另外的割点到达某个关键点,如上图,红

POJ 3352 &amp; 3177 无向图的边-双连通分量(无重边 &amp; 重边)

无向图的边-双连通分量 无向图的双连通分量实际上包含两个内容:点-双连通分量.边-双连通分量 点-双连通分量是指:在该连通分量里面,任意两个点之间有多条点不重复的路径(不包括起点.终点) 边-双连通分量是指:在该连通分量里面,任意两个点之间有多条边不重复的路径 在求解点-双连通分量时,无向图有没有重边都没有关系,因为一个点只能经过一次(有重边也无妨) 该篇文章并不深入讨论点-双连通分量,给出代码给有兴趣的参考参考:(也可以看看POJ2942这道题, 解题报告) /*===============

UVALive - 5135 Mining Your Own Business(双连通分量)

题目大意:有N个矿井 ,由一些隧道连接起来,现在要修建尽量少的安全通道,使得无论哪里发生事故,所有人均能逃出,求建的最少的安全通道数量和方案数 解题思路:建安全通道的话,肯定不能建在割顶,因为割顶如果崩塌了,割顶所连接的双连通分量内的点就跑不掉了,还得在双连通分量里面再建点(上述为双连通分量内部只有一个割顶的情况),这样不划算,还不如直接在里面建点 如果一个双连通分量的内部割顶有多个的话,那么在这个双连通分量里面就可以不用建安全通道了,因为一个割顶崩塌了,还有其他点可以连向外面,所以,只考虑内部

[noip2011]铺地毯(carpet)解题报告

最近在写noip2011的题,备战noip,先给自己加个油! 下面是noip2011的试题和自己的解题报告,希望对大家有帮助,题目1如下 1.铺地毯(carpet.cpp/c/pas) [问题描述]为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯.一共有n 张地毯,编号从1 到n.现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上.地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的

HDU 4612——Warm up——————【边双连通分量、树的直径】

Warm up Time Limit:5000MS     Memory Limit:65535KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4612 Description N planets are connected by M bidirectional channels that allow instant transportation. It's always possible to travel bet

UVALive - 3523 Knights of the Round Table(无向图的双连通分量)

题目大意:有n个骑士经常举行圆桌会议,每次圆桌会议至少要有3个骑士参加(且每次参加的骑士数量是奇数个),且所有互相憎恨的骑士不能坐在圆桌旁的相邻位置,问有多少个骑士不可能参加任何一个会议 解题思路:以骑士为点建立无向图G.如果两个骑士可以相邻(即他们并不互相憎恨),即可连一条边. 则题目就转化为求不在任何一个简单奇圈上的结点个数 首先,圈就是一个双连通的分量,所以第一件事就是将所有的双连通分量求出来,接着再判定这个双连通分量是不是奇圈 奇圈的判定就是用二分图染色判定,如果某个圈能被二分图染色,那

CodeVS第四次月赛解题报告

1.   奶牛的身高 题目描述 Description 奶牛们在FJ的养育下茁壮成长.这天,FJ给了奶牛Bessie一个任务,去看看每个奶牛场中若干只奶牛的身高,由于Bessie是只奶牛,无法直接看出第i只奶牛的身高,而只能看出第i只奶牛与第j只奶牛的身高差,其中第i只奶牛与第j只奶牛的身高差为Ai(i<=n).当A大于0时表示这只奶牛比前一只奶牛高A cm,小于0时则是低.现在,FJ让Bessie总共去看了m次身高,当然也就传回给FJ m对奶牛的身高差,但是Bessie毕竟是奶牛,有时候眼睛可

USACO Section1.2 Dual Palindromes 解题报告

dualpal解题报告 —— icedream61 博客园(转载请注明出处)------------------------------------------------------------------------------------------------------------------------------------------------[题目] 给出N和S,找出大于S的前N个双回文数. 双回文数定义:在二进制至十进制中的两种(或两种以上)进制下是回文数.[数据范围] 1

POJ - 3352 Road Construction(边双连通分量)

题目大意:给出一张无向图,问添加多少边才能使得这张无向图变成边双连通分量 解题思路:先求出所有的边双连通分量,再将边双连通缩成一个点,通过桥连接起来,这样就形成了一棵无根树了 现在的问题是,将这颗无根树变成边双连通分量 网上的解释是:统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf.则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2.具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径