la3523+la5135

本文详细介绍了如何使用双连通分量算法解决两类问题:一是计算骑士间不存在相邻组合的方式数量;二是确定安全设备的最佳放置位置。通过具体实例展示了算法实现过程,并给出完整代码。

题意:

la3523:

骑士之间存在两两厌恶,不可为邻(围着圆桌坐),现求有几种其数个组合方式

思路:

就是求奇数圈.那么就是求点双连通分量

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 1005;
const int maxx = 1000005;
int n, m;
int a[maxn][maxn];
int dfn[maxn], low[maxn], iscut[maxn], bccno[maxn];
int scnt, stack[maxx], bcc_cnt;
int stamp;
int len;
vector<int> vec[maxn], bcc[maxn];
struct edge {
	int u, v;
	edge(int x, int y) {
		u = x;
		v = y;
	}
	edge(){}
}e[maxx];
void init() {
	memset(a, 0, sizeof(a));
	for(int i=1; i<=n; i++) {
		vec[i].clear();
	}

	memset(dfn, 0, sizeof(dfn));
	memset(bcc, 0, sizeof(bcc));
	memset(low, 0, sizeof(low));
	memset(iscut, 0, sizeof(iscut));
	memset(bccno, 0, sizeof(bccno));
	stamp = scnt = bcc_cnt = 0;

}

void tarjan(int cur, int fa) {
	int child = 0, tmp;
	dfn[cur] = low[cur] = ++stamp;

	for(int i=0; i<vec[cur].size(); i++) {
		int v = e[vec[cur][i]].v;
		if(!dfn[v]) {
			stack[++scnt] = vec[cur][i];
			child ++;
			tarjan(v, cur);
			low[cur] = min(low[cur], low[v]);//scc的根源时间
			if(low[v] >= low[cur]) {//原理很简单,一旦绕成环,那么后面的点会比相对前面的除了第一个点之外的点大
				iscut[cur] = 1;//割点
				bcc[++bcc_cnt].clear();//bcc_cnt是双连通分量的数量
				while(1) {
					int num = stack[scnt--];
					if(bccno[e[num].u]!=bcc_cnt) {//存储所有在连通分量上的点
						bcc[bcc_cnt].push_back(e[num].u);
						bccno[e[num].u] = bcc_cnt;
					}
					if(bccno[e[num].v] != bcc_cnt) {
						bcc[bcc_cnt].push_back(e[num].v);
						bccno[e[num].v] = bcc_cnt;
					}

					if(e[num].u == cur && e[num].v == v) {
						break;//直到为割点为止
					}
				}
			}
		}
		else if(dfn[v]<dfn[cur] && v!=fa) {
			stack[++scnt] = vec[cur][i];
			low[cur] = min(low[cur], dfn[v]);
		}
	}
	if(fa<0 && child==1) iscut[cur] = 0;
}

void find_bcc() {
	for(int i=1; i<=n; i++) {
		if(!dfn[i]) tarjan(i, -1);
	}
}
int odd[maxn], color[maxn], b;
bool dfs(int cur, int c) {
	if(bccno[cur] != b) return 1;//不再这个连通分量内
	color[cur] = c;
	for(int i=0; i<vec[cur].size(); i++) {
		int tmp = e[vec[cur][i]].v;
		if(color[cur] == color[tmp]) return 0;//如果在同一个集合则不是二分图,如果不是二分图就是奇数圈
		if(!color[tmp] && !dfs(tmp, 3-c)) return 0;//不一条线的两个点涂成不同的颜色
	}
	return 1;
}

int main() {
	int x, y;
	while(scanf("%d%d", &n, &m) && (n+m)) {
		init();
		len = 0;
		for(int i=0; i<m; i++) {
			scanf("%d%d", &x, &y);
			a[x][y] = a[y][x] = 1;
		}
		for(int i=1; i<=n; i++) {
			for(int j=i+1; j<=n; j++) {
				if(!a[i][j]) {
					e[len] = edge(i,j);
					vec[i].push_back(len);
					len++;
					e[len] = edge(j,i);
					vec[j].push_back(len);
					len++;
				}
			}
		}
		find_bcc();
		int ans = 0;
		memset(odd, 0, sizeof(odd));
		for(int i=1; i<=bcc_cnt; i++) {
			memset(color, 0, sizeof(color));
			b = i;
			for(int j=0; j<bcc[i].size(); j++) bccno[bcc[i][j]] = i;
			if(!dfs(bcc[i][0], 1)) 
				for(int j=0; j<bcc[i].size(); j++) odd[bcc[i][j]] = 1;
		}
		int flag = 0;

		for(int i=1; i<=n; i++) {
			
			if(!odd[i]) {
				ans++;
			}
		}
		printf("%d\n", ans);
		
	}

return 0;
}

la5313

题意:

据说是final,题目讲的就是给你井的位置,然后让你在每个地方放安全设备,要求处于每个井的工人都能在某一个地方出问题是得到安全设备

思路:

首先求双连通分量,然后如果这个连通分量只有一个割点,那么这个连通分量就只需要在一个除了割点的地方安放设备,如果有多个割点,那么无须安放,因为可以去别的连通分量里拿,如果只有一个连通分量,那么一共就只要安装两个,因为防止被爆

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <stack>
using namespace std;
#define N 100010
#define INF 0x3f3f3f3f
#define ll long long

struct Edge{
    int to, next;
}E[N];

struct Node {
    int u, v;
    Node() {}
    Node(int u, int v): u(u), v(v) {}
};

int pre[N], iscut[N], bccno[N], head[N], dfs_clock, bcc_cnt;
int n, tot, Max;
vector<int> bcc[N];
stack<Node> S;
void add(int a, int b) {
	E[tot].to = b;
	E[tot].next = head[a];
	head[a] = tot++;
	E[tot].to = a;
	E[tot].next = head[b];
	head[b] = tot++;
}

void input() {
	int a, b;
	for(int i=0; i<n; i++) {
		scanf("%d %d", &a, &b);
		a--, b--;
		add(a, b);
		
		Max = max(Max, max(a, b));
	}
}

int dfs2(int cur, int fa) {
	int lowu;
	pre[cur] = lowu = ++dfs_clock;
	int child = 0;
	for(int i=head[cur]; i!=-1; i=E[i].next) {
		int v = E[i].to;
		
		Node e = Node(cur, v);
		if(!pre[v]) {
			child++;
			S.push(e);
			int lowv = dfs2(v,cur);
			lowu = min(lowu, lowv);
			if(lowv >= pre[cur]) {
				iscut[cur] = 1;
				bcc_cnt++;
				bcc[bcc_cnt].clear();
				while(1) {
					Node t = S.top();
					S.pop();
					if(bccno[t.u] != bcc_cnt) {
						bcc[bcc_cnt].push_back(t.u);
						bccno[t.u] = bcc_cnt;
					}
					if(bccno[t.v] != bcc_cnt) {
						bcc[bcc_cnt].push_back(t.v);
						bccno[t.v] = bcc_cnt;
					}
					if(t.u == cur && t.v == v) {
						break;
					}
				}
			}
		}
		else if(pre[v] < pre[cur] && v != fa){
			lowu = min(lowu, pre[v]);
			S.push(e);
		}
	}
	if(child==1 && fa<0) iscut[cur] = 0;
	return lowu;
}



int cas = 1;
void init2() {
	memset(pre, 0, sizeof(pre));
	memset(bccno, 0, sizeof(bccno));
	memset(iscut, 0, sizeof(iscut));
	memset(head, -1, sizeof(head));
	dfs_clock = bcc_cnt = tot = Max = 0;
}
void deal() {
	
	for(int i=0; i<=Max; i++) {
		if(!pre[i])
		dfs2(i, -1);
	}
	long long ans1 = 0, ans2 = 1;
	for(int i=1; i<=bcc_cnt; i++) {
		int cnt_cut = 0;
		for(int j=0; j<bcc[i].size(); j++) {
			if(iscut[bcc[i][j]]) {
				cnt_cut++;
			}
		}
		if(cnt_cut == 1) {
			ans1++;
			ans2 *= (long long)(bcc[i].size()-1);
		}
	} 
	if(bcc_cnt == 1) {
		ans1 = 2;
		ans2 = (long long)(bcc[1].size()*(bcc[1].size()-1)/2);
	}
	printf("Case %d: %lld %lld\n", cas++, ans1, ans2);

}

int main() {
    while(scanf("%d", &n) != EOF && n) {
		init2();
        	input();
		deal();
    }
    return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值