[OpenJudge 3063]罪犯问题
试题描述
一天,警官抓获了N个嫌犯,审问N个罪犯的途中,作为警长助手的你突然发现其中被确定为罪犯的K号人是你曾经出生入死的兄弟,你不能眼睁睁看着他被抓进牢里。
审问完了嫌犯之后,发现每个人都说话了,并且每个人只说了一句话,说的话形式有两种,“XXX是罪犯”以及“XXX不是罪犯”,并且罪犯说的都是假话,不是罪犯的说的都是真话(每人说的话均不是说自己)。
因为见过其中M个人的通缉令(不包括你的兄弟),镇长可以确定这M个人是罪犯。
通过这些情况可推断出大部分人是不是罪犯。每个人说话的内容都已经存入了资料库之中,现在你需要冒险修改资料库中某些人说话的内容使得你兄弟摆脱的罪犯嫌疑(必须修改后确定他不是罪犯)。
当然修改后的资料库数据不能存在矛盾,修改的条数越多,风险也就越大,现在希望你能求出最少要修改资料库中几个人说的话。
输入
第一行,三个整数N,M,K,分别表示嫌犯个数,被确定的罪犯个数以及你兄弟的编号。
第二行,M个整数,第i个整数Ti表示编号Ti的嫌犯确定是罪犯。
第3-N+2行,第i+2行有一个整数X,若X大于零,表示编号为i嫌犯的人说“X号是罪犯”;若X小于零,表示编号i为嫌犯的居民说“-X号不是罪犯”。
对于100%的数据,N<=200000,1<=M<=N;
数据保证嫌犯说的话不存在矛盾,且K号本为罪犯且未在通缉令上。
输出
仅一个整数,表示最少需修改资料库中几人说的话,使得你兄弟摆脱罪犯的嫌疑。
输入示例
3 1 3 1 -2 -3 -1
输出示例
2
数据规模及约定
见“输入”
题解
这题挺绕的,我题意理解了半天。稍加分析发现给出的信息是正的还是负的并不影响最终答案,因为题目中保证了输入合法。
那么对于一条给出的信息“x 说 y (不)是罪犯”,其实是告诉我们,如果确定了 x 的身份,则能够确定 y 的身份;反之,如果知道了 y 的身份,就进一步能确定 x 的身份。那么现在有 m 个人的身份已知,目的是不能通过这 m 个已知的人的身份确定 k 的身份。再进一步解释就是:对于每一条信息建双向边,割断最少数量的边,使得从给定的 m 个人出发,没有一条路径能够到达 k;这显然就是一个最小割了,建一个超级源点向给定的 m 人连容量无穷的边,再把 k 看成汇点,跑一边最小割就行了。
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <vector> #include <queue> #include <cstdlib> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 200010 #define maxm 801010 #define oo 2147483647 struct Edge { int from, to, flow; } ; struct Dinic { int n, m, s, t, head[maxn], next[maxm]; Edge es[maxm]; int vis[maxn], Q[maxn], hd, tl; int cur[maxn]; void init(int _) { n = _; m = 0; memset(head, -1, sizeof(head)); return ; } void AddEdge(int a, int b, int c) { es[m] = (Edge){ a, b, c }; next[m] = head[a]; head[a] = m++; return ; } bool BFS() { memset(vis, 0, sizeof(vis)); hd = tl = 0; Q[++tl] = s; vis[s] = 1; while(hd < tl) { int u = Q[++hd]; for(int i = head[u]; i != -1; i = next[i]) { Edge& e = es[i]; if(!vis[e.to] && e.flow) { vis[e.to] = vis[u] + 1; Q[++tl] = e.to; } } } return vis[t] > 0; } int DFS(int u, int a) { if(u == t || !a) return a; int flow = 0, f; for(int& i = cur[u]; i != -1; i = next[i]) { Edge& e = es[i]; if(vis[e.to] == vis[u] + 1 && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MinCut(int _, int __) { s = _; t = __; int flow = 0; while(BFS()) { for(int i = 1; i <= n; i++) cur[i] = head[i]; flow += DFS(s, oo); } return flow; } } sol; int main() { int n = read(), m = read(), k = read(); sol.init(n + 2); int s = n + 1, t = n + 2; for(int i = 1; i <= m; i++) { int x = read(); sol.AddEdge(s, x, oo), sol.AddEdge(x, s, 0); } for(int i = 1; i <= n; i++) { int x = abs(read()); sol.AddEdge(i, x, 1); sol.AddEdge(x, i, 1); } sol.AddEdge(k, t, oo); sol.AddEdge(t, k, 0); printf("%d ", sol.MinCut(s, t)); return 0; }