狼人杀( 强连通 脑洞题)

原题 : hdu 6370

题意 :

n个人玩狼人杀,每个人会说另外一个人是狼还是村民,狼可以说假话,村民不行

求最后的只能是村民的数量,只能是狼的数量(不确定的不管)

解析 :

首先,每个人都可以是狼,不管对方是什么你说什么都不会错(狼可以说真话和假话)

那么我们要做的就是怎么样判断一个人能不能是村民

下面就是不能是村民的两种案例(v村民,w狼人)
这里写图片描述
在一个环中,如果只有一个人说狼人,那么那个人说的人一定是狼人(自推)

而在这个人是狼人的基础上,说这个人是村民的那个也一定是狼人

所以我们用tarjan做一下环,就可以了

注意,环中的那个确定的狼人,指向他的那些说他是村民的节点的处理,一定要从这个点开始往外dfs,如果从支链的末端开始找的话会TLE (比赛的时候觉得支链比较短就从支链那边做了,结果TLE卡死了,心态爆炸,比赛的时候真的令人绝望,心态爆炸…)

代码:

#include<bits/stdc++.h>
using namespace std;
#define D long long
#define ch(i) printf("%d\n",i)
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}


const int N=100009;
int to[N],f[N],in[N];//wolf =1

int n,numque=0,head[N],cnt,dfn[N],low[N],now,belong[N];
queue<int> que[N];
stack<int> sa;
struct edge{
    int next,to;
}e[N];
void add(int u,int v){
    e[cnt].to=v,e[cnt].next=head[u];
    head[u]=cnt++;
}
void init(){
    memset(head,-1,sizeof(head));
    memset(in,0,sizeof(in));
    numque=0,cnt=0,now=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(belong,0,sizeof(belong));
    while(!sa.empty()) sa.pop();

}
void tarjan(int x){
    dfn[x]=low[x]=++now;
    sa.push(x);
    for(int i=head[x];~i;i=e[i].next){
        int v=e[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(!belong[i]) low[x]=min(low[x],low[v]);
    }
    if(dfn[x]==low[x]){
        numque++;
        while(1){
            int a=sa.top(); sa.pop();
            belong[a]=numque;
            que[numque].push(a);
            if(a==x) break;
        }
    }
}

vector<int>re[N];

void dfs(int p,int &ans){
    for(int i=0;i<re[p].size();i++){
        int from=re[p][i];
        if(f[from]==0){
            ans++;dfs(from,ans);
        }
    }
}

int cant[N];
int vis[N];
int main(){
    int t=read();
    while(t--){
        memset(vis,0,sizeof(vis));
        memset(in,0,sizeof(in));
        memset(cant,0,sizeof(cant));
        init();
        numque=0;
        int fans=0;
        n=read();
        for(int i=1;i<=n;i++)re[i].clear();
        for(int i=1;i<=n;i++){
            to[i]=read();
            in[to[i]]++;add(i,to[i]);
            re[to[i]].push_back(i);
            char xx[10];scanf("%s",xx);
            if(xx[0]=='w')f[i]=1;
            else f[i]=0;
        }


        for(int i=1;i<=n;i++){
            if(!dfn[i]) tarjan(i);
        }

        for(int i=1;i<=numque;i++){
            if(que[i].size()==1)continue;
            int sayw=-1,numw=0;

            int p=que[i].front();int ro=p;
            vis[p]=1;
            while(1){
                if(f[p]==1){
                    numw++;
                    sayw=p;
                }
                p=to[p];
                vis[p]=1;
                if(p==ro)break;
            }
            if(numw==1)cant[to[sayw]]=1,dfs(to[sayw],fans),fans++;
        }


        for(int i=1;i<=numque;i++){
            while(!que[i].empty()) que[i].pop();
        }


        printf("%d %d\n",0,fans);

    }
}



*/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值