大意不再赘述。
思路:在本题中,每个人都有两种状态,可以据此将两个人拆点成p0,p1,分别表示当选与落选。
i和j至少有一个人当选:i0->j1和j0->i1.
i和j至少有一个人落选:i1->j0,j1->i0.
i当选,j落选:i0->j0,j1->i1.
i落选,j当选:i1->j1,j0->i0.
大致就这么几种情况,分别连边之后判断有无点在同一个强连通分量里就OK。
拆点为2*x, 2*x+1, 前者表示落选,后者表示当选,由于数据从1开始,所以处理的时候自减1。
注意:数组开大点,顶点数组开的1010,TLE,边数组开的1000010会RE。
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <string> using namespace std; const int MAXN = 2010; const int MAXM = 1000010; const int INF = 0x3f3f3f3f; struct Edge { int v, next, w; }edge[MAXM*4]; int first[MAXN], dfn[MAXN], low[MAXN], stack[MAXN], ins[MAXN]; int belong[MAXN]; int n, m; int cnt; int scnt, tot, top; inline void init() { cnt = 0; scnt = tot = top = 0; memset(first, -1, sizeof(first)); memset(ins, 0, sizeof(ins)); memset(dfn, 0, sizeof(dfn)); } inline void read_graph(int u, int v) { edge[cnt].v = v; edge[cnt].next = first[u], first[u] = cnt++; } void read_case() { init(); char c1, c2; for(int i = 0; i < m; i++) { int x, y; scanf(" %c%d %c%d", &c1, &x, &c2, &y); --x; --y; if(c1 == '+') { if(c2 == '+') // + + { read_graph(2*x, 2*y+1); read_graph(2*y, 2*x+1); } else // + - { read_graph(2*x, 2*y); read_graph(2*y+1, 2*x+1); } } else { if(c2 == '+') // - + { read_graph(2*x+1, 2*y+1); read_graph(2*y, 2*x); } else // - - { read_graph(2*x+1, 2*y); read_graph(2*y+1, 2*x); } } } } void dfs(int u) { int v; low[u] = dfn[u] = ++tot; ins[u] = 1; stack[top++] = u; for(int e = first[u]; e != -1; e = edge[e].next) { v = edge[e].v; if(!dfn[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if(ins[v]) { low[u] = min(low[u], dfn[v]); } } if(low[u] == dfn[u]) { scnt++; do { v = stack[--top]; belong[v] = scnt; ins[v] = 0; }while(u != v); } } void Tarjan() { for(int v = 0; v < 2*n; v++) if(!dfn[v]) dfs(v); } void solve() { Tarjan(); for(int i = 0; i < 2*n; i += 2) { if(belong[i] == belong[i+1]) { printf("0\n"); return ; } } printf("1\n"); } int main() { while(~scanf("%d%d", &n, &m)) { read_case(); solve(); } return 0; }