前缀和优化建图2-SAT

本文最后更新于:星期四, 二月 3日 2022, 9:15 晚上

BZOJ #3495. PA2010 Riddle

题目描述

nn个城镇被分成了kk个郡,有m条连接城镇的无向边。

要求给每个郡选择一个城镇作为首都,满足每条边至少有一个端点是首都。

思路

每个城市只有是或者不是两个状态,很显然的2SAT2-SAT,其中MM条边(u,v)(u, v)很显然建边uv,vuu'\rightarrow v, v'\rightarrow u

对于每个郡只有一个城镇是首都

我们考虑前缀和。

首先新增nn个点,对于ii对应的新的节点为i+ni+n。那么我们的i+ni+n表示当前郡的前ii个是否应有城市是否为首都,那么如果i1+ni-1+n选了,i+ni+n必定选了,i+ni+n没选,i1+ni-1+n必定没选。同时如果ii选了,那么i1+ni-1+n必定没选,如果i1+ni - 1 + n选了,ii一定没选

我们令u1=i1,u2=i,v1=i1+n,v2=i+nu1=i-1,u2=i,v1=i-1+n,v2=i+n

那么需要建边

  • v1v2v1\rightarrow v2
  • v2v1v2'\rightarrow v1'
  • u2v1u2\rightarrow v1'
  • v1u2v1 \rightarrow u2'

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

#include <bits/stdc++.h>

using namespace std;

#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)

void err() { cout << endl; }

template<class T, class... Ts>
void err(const T &arg, const Ts &... args) {
cout << arg << ' ';
err(args...);
}

#define endl "\n"
using ll = long long;
using db = double;

const int N = 4e6 + 10;

struct Edge {
int to, nxt;

Edge() {}

Edge(int to, int nxt) : to(to), nxt(nxt) {}
} edge[N << 1];

int head[N], tot;

void Init() {
memset(head, -1, sizeof head);
tot = 0;
}

void addedge(int u, int v) {
edge[tot] = Edge(v, head[u]);
head[u] = tot++;
}

int Low[N], DFN[N], Stack[N], Belong[N];
int Index, top;
int scc;
bool Instack[N];
int num[N];

void Tarjan(int u) {
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
for (int i = head[u]; ~i; i = edge[i].nxt) {
v = edge[i].to;
if (!DFN[v]) {
Tarjan(v);
if (Low[u] > Low[v]) Low[u] = Low[v];
} else if (Instack[v] && Low[u] > DFN[v]) {
Low[u] = DFN[v];
}
}
if (Low[u] == DFN[u]) {
scc++;
do {
v = Stack[--top];
Instack[v] = false;
Belong[v] = scc;
num[scc]++;
} while (u != v);
}
}

bool solve(int n) {
memset(DFN, 0, sizeof DFN);
memset(Instack, false, sizeof Instack);
memset(num, 0, sizeof num);
Index = scc = top = 0;
for (int i = 0; i < n; ++i) {
if (!DFN[i]) {
Tarjan(i);
}
}
for (int i = 0; i < n; i += 2) {
if (Belong[i] == Belong[i ^ 1]) {
return false;
}
}
return true;
}

int n, m, k;
int a[N];

void RUN() {
while (scanf("%d %d %d", &n, &m, &k) != EOF) {
Init();
for (int i = 1, x, y; i <= m; ++i) {
scanf("%d %d", &x, &y);
--x, --y;
addedge(x << 1 ^ 1, y << 1);
addedge(y << 1 ^ 1, x << 1);
}
for (int i = 0; i < n; ++i) {
int x = i, y = i + n;
addedge(x << 1, y << 1);
addedge(y << 1 ^ 1, x << 1 ^ 1);
}
for (int i = 1, w; i <= k; ++i) {
scanf("%d", &w);
for (int j = 1; j <= w; ++j) {
scanf("%d", a + j);
--a[j];
}
for (int j = 2; j <= w; ++j) {
int x = a[j - 1], y = a[j];
addedge((x + n) << 1, (y + n) << 1);
addedge(((y + n) << 1) ^ 1, ((x + n) << 1) ^ 1);
addedge(y << 1, (x + n) << 1 ^ 1);
addedge((x + n) << 1, y << 1 ^ 1);
}
}
puts(solve(4 * n) ? "TAK" : "NIE");
}
}

int main() {
#ifdef LOCAL_JUDGE
freopen("input.txt", "r", stdin);
#endif

// ios::sync_with_stdio(false);
// cin.tie(nullptr), cout.tie(nullptr);

RUN();

#ifdef LOCAL_JUDGE
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
return 0;
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!