SP2878 KNIGHTS - Knights of the Round Table [Tarjan, 奇环]

KNIGHTSKNIGHTS

题目描述见链接 .


\color{red}{正解部分}

将原图的 补图 建出,
那么当一个节点被某个奇环覆盖时, 就存在一个方案, 使得这个节点参加会议 .
当得到所有被至少一个奇环覆盖的节点数量 cntcnt 后, 使用 总结点数量 减去 cntcnt 即可得到答案 .
那么现在的问题就是 cntcnt 如何求,

首先要知道 22 个引理 \downarrow

  • 不同 点双联通分量 之间不会被同一个 奇环 覆盖 .

若存在一个奇环沟通两个 点双联通分量, 那么这两个 点双联通分量 可以合并 .

  • 在一个 点双联通分量 中, 若存在一个 奇环, 则所有点被至少一个 奇环 覆盖 .

设在 点双联通分量 中的 奇环 中存在两个点 u,vu, v, 奇环 外部存在一个点 ww, 由于 奇环u,vu, v 存在两条 奇偶性 不同的路径, u,v,wu,v,w 可以构成 奇环偶环 .

知道这 22 个引理之后, 就可以做这道题目了 .

使用 TarjanTarjan 缩点, 判断每个 点双联通分量 中是否存在奇环即可, 二分图 中不存在 奇环, 二染色 来判断是否 二分图 即可 .


\color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register
#define pb push_back

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 1005;

int N;
int M;
int tim;
int Ans;
int num0;
int bk_cnt;
int low[maxn];
int dfn[maxn];
int head[maxn];
int bk_Mp[maxn];
int color[maxn];

bool Used[maxn];
bool In_stk[maxn];
bool is[maxn][maxn];

std::stack <int> stk;
std::stack <int> stk_2;

std::vector <int> bk[maxn];

struct Edge{ int nxt, to; } edge[maxn*maxn<<1];

void Add(int from, int to){ edge[++ num0] = (Edge){ head[from], to }; head[from] = num0; }

void Tarjan(int k, int fa){
        low[k] = dfn[k] = ++ tim; 
        for(reg int i = head[k]; i; i = edge[i].nxt){
                int to = edge[i].to;
                if(!dfn[to]){
                        stk.push(k), stk_2.push(to);
                        Tarjan(to, k), low[k] = std::min(low[k], low[to]);
                        if(low[to] < dfn[k]) continue ;
                        bk_cnt ++;
                        while(!stk.empty()){
                                int tp = stk.top(), tu = stk_2.top();
                                bk[bk_cnt].pb(tp), bk[bk_cnt].pb(tu);
                                stk.pop(), stk_2.pop();
                                if(tp == k && tu == to) break ;
                        }
                }else if(dfn[to] < dfn[k] && to != fa){
                        stk.push(k), stk_2.push(to);
                        low[k] = std::min(low[k], dfn[to]);
                }
        }
}

int col_cnt;

bool DFS(int k, int col, int id){
        color[k] = col;
        for(reg int i = head[k]; i; i = edge[i].nxt){
                int to = edge[i].to;
                if(id != bk_Mp[to]) continue ;
                if(color[k] == color[to]) return false;
                if(color[to] == -1){
                        color[to] = col ^ 1;
                        if(!DFS(to, col^1, id)) return false;
                }
        }
        return true;
}

void Work(){
        tim = num0 = 0;
        for(reg int i = 1; i <= N; i ++) Used[i] = head[i] = dfn[i] = 0, color[i] = -1;
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 1; j <= N; j ++) is[i][j] = 0;
        for(reg int i = 1; i <= M; i ++){
                int a = read(), b = read();
                is[a][b] = is[b][a] = 1;
        }
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = i+1; j <= N; j ++)
                        if(!is[i][j]) Add(i, j), Add(j, i);
        for(reg int i = 1; i <= N; i ++) if(!dfn[i]) Tarjan(i, 0);
        for(reg int i = 1; i <= bk_cnt; i ++){
                memset(color, -1, sizeof color);
                for(reg int j = 0; j < bk[i].size(); j ++) bk_Mp[bk[i][j]] = i;
                if(!DFS(bk[i][0], 0, i))
                        for(reg int j = 0; j < bk[i].size(); j ++) Used[bk[i][j]] = 1;
                bk[i].clear();
        }
        Ans = N;
        for(reg int i = 1; i <= N; i ++) Ans -= Used[i];
        printf("%d\n", Ans);
}

int main(){
        N = read(), M = read();
        while(N && M){
                Work();
                N = read(), M = read();
        }
        return 0;
}
posted @ 2019-09-11 12:38  XXX_Zbr  阅读(136)  评论(0编辑  收藏  举报