题意:
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;
}

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

被折叠的 条评论
为什么被折叠?



