题意
有 n(n≤1000) n ( n ≤ 1000 ) 个骑士, m(m≤1000000) m ( m ≤ 1000000 ) 对骑士互相憎恨,要3个以上的骑士且保证互相憎恨的骑士不会坐在相邻位置才能开会议,求多少骑士一定不能参加任何一次会议。
题解
思路其实很简单,找到一个无向环,然后看环是否是奇数环就可以了。看lrj的白书把自己看晕了,下面说一下自己的理解。
一个无向环一定在一个双连通分量中。这是因为双连通分量保证对于其中的任意节点 u,v u , v ,存在两条不相交的路径,使得从 u u 到。所以找无向环就找双连通分量即可。
二分图染色来判断奇数环。因为二分图是不含奇数环的,所以如果一个双连通分量能被二染色,他一定不含奇数环,所以就不满足题意,所以这些点就不能算入到能够参加圆桌会议的点中。lrj的白书花了大量篇幅来证明为什么不是二分图一定能保证这些是能参加圆桌会议的点。
割点可以在多个无向环中,也就是可以在多个奇数环中。这点不要误解316页的注释。注释的意思是指,每个环必须是无向简单环,也就是环中的点不存在自环。如果存在自环,那么说明骑士可以和自己相邻。这显然是不符合常理的。重点是这句话:因为同一个骑士不能在圆桌上出现两次。他并不是说割点不能参加两次圆桌会议,其实指的是上面自环的问题。
如果明白上面的点,那基本上就没啥问题了。找出所有bcc,然后对每个bcc二分色,如果是奇数环,那么可以参加圆桌会议,标记;反之不可以,最后统计答案。
代码
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e6+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
int mp[1005][1005];
int n, m;
int head[1005], tot;
int dfn[1005], low[1005], st, dfs_clock, bcc_cnt, bccno[1005], color[1005];
int ok[1005], thisok, nowbcc;
//bool iscut[nmax];
struct edge {
int to, nxt;
}e[nmax << 1];
typedef pair<int, int> pii;
pii sstack[nmax];
vector<int> bcc[1005];
void add_edge(int u, int v) {
e[tot].to = v;
e[tot].nxt = head[u];
head[u] = tot ++;
}
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++ dfs_clock;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(!dfn[v]) {
sstack[st++] = make_pair(u, v);
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) {
// iscut[u] = true;
bcc_cnt++;
bcc[bcc_cnt].clear();
for(;;) {
pii tmp = sstack[--st];
if(bccno[tmp.first] != bcc_cnt) {
bccno[tmp.first] = bcc_cnt;
bcc[bcc_cnt].push_back(tmp.first);
}
if(bccno[tmp.second] != bcc_cnt) {
bccno[tmp.second] = bcc_cnt;
bcc[bcc_cnt].push_back(tmp.second);
}
if(tmp.first == u && tmp.second == v || tmp.first == v && tmp.second == u)
break;
}
}
} else if( v != fa && dfn[v] < dfn[u]) {
sstack[st++] = make_pair(u, v);
low[u] = min(low[u], dfn[v]);
}
}
}
void getcolor(int u, int nowcolor) {
color[u] = nowcolor;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(bccno[v] == nowbcc) {
if(!color[v]) {
getcolor(v, 3 - nowcolor);
} else if(color[v] == nowcolor)
thisok = false;
}
if(!thisok)
return;
}
}
int main(){
// freopen("out.txt", "w", stdout);
while( scanf("%d %d", &n, &m) != EOF) {
if(n == 0 && m == 0)
break;
else {
memset(head, -1, sizeof head);
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(mp, 0, sizeof mp);
memset(bccno, 0, sizeof bccno);
memset(ok, 0, sizeof ok);
// memset(sstack, 0, sizeof sstack);
nowbcc = thisok = dfs_clock = tot = st = bcc_cnt = 0;
}
int u, v;
n = 5;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
mp[i][j] = 1;
}
}
mp[1][2] = mp[1][3] = mp[2][3] = 0;
mp[3][4] = mp[3][5] = mp[4][5] = 0;
// for(int i = 1; i <= m; ++i) {
// scanf("%d %d", &u, &v);
// mp[u][v] = mp[v][u] = 1;
// }
for(int i = 1; i <= n; ++i) {
for(int j = i+1; j <= n; ++j) {
if(mp[i][j] == 0) {
add_edge(i, j);
add_edge(j, i);
}
}
}
for(int i = 1; i <= n; ++i) {
if(!dfn[i])
tarjan(i, -1);
}
// for(int i = 1; i <= n; ++i) {
// if(iscut[i] == true) {
// printf("debug %d\n", i);
// }
// }
for(int i = 1; i <= bcc_cnt; ++i) {
memset(color, 0, sizeof color);
// printf("%d :", i);
for(int j = 0; j < bcc[i].size(); ++j) {
bccno[bcc[i][j]] = i;
// printf(" %d", bcc[i][j]);
}
// printf("\n");
thisok = true;
nowbcc = i;
getcolor(bcc[i][0], 1);
if(!thisok) {
for(int j = 0; j < bcc[i].size(); ++j) {
ok[bcc[i][j]] = true;
}
}
}
int ans = 0;
for(int i = 1; i <= n; ++i) {
if(ok[i] == 0)
ans ++;
}
printf("%d\n", ans);
// for(int i = 0; i <= bcc_cnt; ++i)
// bcc[i].clear();
}
return 0;
}