BZOJ 2730 [HNOI2012]矿场搭建 (割点)

一开始写这题,心想的就是贪心去写,但是bzoj上一直wa,心想能拿到一点分吧,就去洛谷找这题,交一发,发现0分。。。wa的一声就哭了

然后看别人题解去了,原来是求割点的题,没写过(摔

然后百度了一波模板题写了一法写的好辛苦。。。

然后终于回到这题了。我的思路是:求出割点,你会发现割点根本不可以建救援点。所以把割点全部去掉,然后跑一边整个图看看有多少个联通块就好了。然后只有30分(

但是这里有个问题,就是如果一个联通块同时连着两个或以上的割点,那么他自己本身肯定是不用救援点的,我们统计的时候就不用加入这个联通块里面的情况。

比如一组数据

6
1 2
2 4
2 3
3 5
3 6
4 5

你可以堵住2这个割点,你会发现4 5组成的连通块是可以到达6这个连通块出去的,堵住3也一样。

然后稍加修改,就满分了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
set <int> s;
vector <int> G[505];
int n;
ll cnt;
bool is_cut[505], vst[505];
int dfn[505], low[505], par[505], ind;

void Tarjan(int u, int Father)
{
	par[u] = Father;
    dfn[u] = low[u] = ind++;
    int son = 0;
    for(int j = 0; j < G[u].size(); ++j)
    {
        int v = G[u][j];
        if(v == Father)                           //只能向下遍历 
        	continue;
        if(dfn[v] == -1)
        {
        	son++;                                //若u为割点,可以分出的联通块数目,该项只对树根有效 
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(u != Father && low[v] >= dfn[u]) { //当u不是树根时,如果存在low >= dfn这种情况
            	is_cut[u] = 1;					  //说明u是一个割点,并且记录下其分割出来的联通块的数目 
			}
        }
        else if(v != Father)
            low[u] = min(low[u], dfn[v]);
    }
    if(u == Father && son > 1)                     //如果u等于father,则说明它是树根。此时若其次数大于2则 
    	is_cut[u] = 1;								//该树根为割点 
}

void dfs(int u) {
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if(is_cut[v]) {
        	s.insert(v);
		} else if (!vst[v]) {
            cnt++;
            vst[v] = 1;
            dfs(v);
        }
    }
}

int main() {
    int Case = 1;
    while(cin >> n) {
        if(n == 0)
            break;
        int Max_ind = 0;
        for(int i = 0; i < 505; i++)
        	G[i].clear();
        memset(is_cut, 0, sizeof(is_cut));
        memset(vst, 0, sizeof(vst));
        memset(dfn, -1, sizeof(dfn));
         
        for(int i = 0; i < n; i++) {
        	int u, v;
            cin >> u >> v;
            Max_ind = max(Max_ind, u);
            Max_ind = max(Max_ind, v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        
        for(int i = 1; i <= Max_ind; i++) {
        	if(dfn[i] == -1)
        		Tarjan(i, i);
    	}
        
        int num = 0;
        ll ans = 1; 
        
        for(int i = 1; i <= Max_ind; i++) {
        	if(!is_cut[i] && !vst[i]) {
        		s.clear();
        		vst[i] = 1;
        		cnt = 1;
        		dfs(i);
        		if(s.size() > 1)
        			continue;
        		num++;
        		ans *= cnt;
        	}
		}
        
        if(Max_ind == 1)
        	printf("Case %d: 1 1\n", Case++);
        else if(num == 1) {
        	ans = (Max_ind * (Max_ind - 1)) / 2;
        	printf("Case %d: 2 %lld\n", Case++, ans);
        }
        else
        	printf("Case %d: %d %lld\n", Case++, num, ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值