题目:
http://www.spoj.com/problems/PARADOX/
题意:
有n个人,每个人都说某个人说的话是真的或者假的,可以说自己,问给出的这些语句矛不矛盾?
思路:
首先,建图时肯定要建有向图,边有值:真或假。用tarjan给每个点标出其所在的环,然后以环中的某个点为起点,随意设其状态真或假(我全部标为真),然后bfs给同一个环中的点标号,bfs过程中遇到不在同一个环的点,直接跳过即可,也就是说决定是否发生矛盾的是同一个环中的点。对于不同环中的点直接跳过的原因,例如m个点组成一个有向环,最少有m条边,两个环之间明显没有边(此处的环中的点数大于1),否则边数大于点数,和题意不符,那么也就是说两个环中必定有一个是单点,这样的话,无论是单点环指向多点环还是多点环指向单点环,都可以从容的给单点环标号,不会发生任何矛盾。
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
struct edge
{
int to, f, next;
}g[N*N*2];
int cnt, head[N];
int f[N];
int scc[N], dfn[N], low[N], scc_sz[N], st[N];
int idx, num, top;
bool vis[N];
void init()
{
cnt = 0;
memset(head, -1, sizeof head);
memset(vis, 0, sizeof vis);
memset(dfn, -1, sizeof dfn);
memset(low, -1, sizeof low);
memset(scc, 0, sizeof scc);
memset(scc_sz, 0, sizeof scc_sz);
idx = top = num = 0;
}
void add_edge(int v, int u, int f)
{
g[cnt].to = u, g[cnt].f = f, g[cnt].next = head[v], head[v] = cnt++;
}
void tarjan(int v)
{
dfn[v] = low[v] = idx++;
st[top++] = v, vis[v] = true;
int u;
for(int i = head[v]; i != -1; i = g[i].next)
{
u = g[i].to;
if(dfn[u] == -1)
{
tarjan(u);
low[v] = min(low[v], low[u]);
}
else if(vis[u]) low[v] = min(low[v], dfn[u]);
}
if(dfn[v] == low[v])
{
num++;
do
{
u = st[--top], vis[u] = false, scc[u] = num, scc_sz[num]++;
}while(u != v);
}
}
bool bfs(int s, int flag)
{
queue<int> que;
que.push(s), f[s] = flag;
while(! que.empty())
{
int v = que.front(); que.pop();
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(scc[u] != scc[v]) continue;
if(f[u] == -1)
{
f[u] = (f[v] ? g[i].f : !g[i].f);
que.push(u);
}
else
{
if(f[u] != (f[v] ? g[i].f : !g[i].f)) return false;
}
}
}
return true;
}
int main()
{
int n, v;
char s[10];
while(scanf("%d", &n), n)
{
init();
for(int i = 1; i <= n; i++)
{
scanf("%d%s", &v, s);
add_edge(i, v, s[0] == 't');
}
for(int i = 1; i <= n; i++)
if(dfn[i] == -1) tarjan(i);
memset(f, -1, sizeof f);
bool flag = true;
for(int i = 1; i <= n; i++)
if(f[i] == -1)
{
flag = bfs(i, 1);
if(! flag) break;
}
puts(flag ? "NOT PARADOX" : "PARADOX");
}
return 0;
}