POJ

洛咕---SP

洛咕---UVA

题意:有\(n\)个骑士经常举行圆桌会议,商讨大事.每次圆桌会议至少有\(3\)个骑士参加,且相互憎恨的骑士不能坐在圆桌的相邻位置.如果发生意见分歧,则需要举手表决,因此参加会议的骑士数目必须是大于1的奇数,以防止赞同和反对票一样多.知道那些骑士相互憎恨之后,你的任务是统计有多少骑士不可能参加任何一个会议.\(n<=1000,m<=10^6\).

分析:建原图的"补图":如果两名骑士没有憎恨关系,则在两个节点之间建立一条无向边.跑无向图\(Tarjan\)求出图中所有的\(v-DCC\),用二分图染色法判定每个\(v-DCC\)中是否存在奇环即可.若存在奇环,则该\(v-DCC\)中的所有骑士都可以参加会议(因为它们必定都在某个奇环中).

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1005;
const int M=1000005;
int n,m,ans,w[N][N];
int tot,head[N],nxt[M],to[M];
int tim,top,cnt,root,BJ;
int dfn[N],low[N],st[M],cut[N];
int belong[N],visit[N],bj[N];
vector<int>dcc[N];
inline void add(int a,int b){nxt[++tot]=head[a];head[a]=tot;to[tot]=b;}
inline void tarjan(int x){
    dfn[x]=low[x]=++tim;st[++top]=x;
    if(x==root&&!head[x]){
        dcc[++cnt].push_back(x);
        return;
    }
    int flag=0;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){
                ++flag;
                if(x!=root||flag>1)cut[x]=1;
                ++cnt;int z;
                do{
                    z=st[top--];
                    dcc[cnt].push_back(z);
                }while(z!=y);
                dcc[cnt].push_back(x);
            }
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
inline void dfs(int x,int color){
    visit[x]=color;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(belong[y]!=belong[x])continue;
        if(!visit[y])dfs(y,3-color);
        else if(visit[y]==color){
            BJ=1;return;
        }
    }
}
int main(){
    while(1){
        n=read();m=read();if(!n&&!m)break;
        tot=0;ans=0;tim=0;top=0;cnt=0;
        for(int i=1;i<=n;++i){
            head[i]=belong[i]=0;
            dfn[i]=low[i]=0;
            cut[i]=bj[i]=st[i]=0;
        }//多组数据初始化,不想用memset...
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)w[i][j]=0;//初始化
        for(int i=1;i<=n;++i)dcc[i].clear();//清空
        for(int i=1;i<=m;++i){
            int a=read(),b=read();
            w[a][b]=w[b][a]=1;//标记憎恨关系
        }
        for(int i=1;i<n;++i)
            for(int j=i+1;j<=n;++j)
                if(!w[i][j])add(i,j),add(j,i);//建立补图
        for(int i=1;i<=n;++i)if(!dfn[i])root=i,tarjan(i);//tarjan求v-dcc
        for(int i=1;i<=cnt;++i){
            for(int j=0;j<dcc[i].size();++j)
                belong[dcc[i][j]]=i,visit[dcc[i][j]]=0;//初始化
            BJ=0;dfs(dcc[i][0],1);//二分图染色法判定是否存在奇环
            if(BJ){//如果存在,标记这个v-dcc内的点不用被踢出去
                for(int j=0;j<dcc[i].size();++j)bj[dcc[i][j]]=1;
            }
        }
        for(int i=1;i<=n;++i)if(!bj[i])++ans;
        printf("%d\n",ans);
    }
    return 0;
}
01-26 20:54