「HNOI2019」校园旅行

#3057. 「HNOI2019」校园旅行

Summarize

给定一个有 $n$ 个节点、 $m$ 条边的简单无向图,每个节点有权值 $0$ 或 $1$。有 $q$ 次询问,每次询问两个节点 $u$、$v$ 间是否存在一条路径(可重复),使经过的节点权值为 $01$ 回文串。

$n \le 5000, m\le 500000, q\le 100000$

Solution

考虑动态规划,记 $f[i][j]$ 表示 $i$、$j$ 两点是否存在回文路径。若 $f[x][y]=1,(x,z)\in E,(y,w)\in E,a[z]=a[w]$,则 $f[z][w]=1$。转移时枚举边转移,时间复杂度 $O(m^2)$。

考虑优化,仅保留等效边。只考虑 连接两个权值相同节点的边 ,可以发现这些边将原图分为若干个连通块。假如只在其中一个联通块中状态转移,其转移方法取决于 该联通块是否为二分图。若是二分图,则连通块内 任意两点间路径长度的奇偶性确定;否则不确定。因此,可以将二分图等效为该图的 生成树,恰好同样符合上述性质;如果不是二分图,则在生成树中添加一个自环即可。

对于 连接两个不同权值节点的边,在任意连通块中显然可以得到其生成树与原图的性质相同。

因此可以将边数降至 $O(n)$ 级别,就可做了。

Note

不知道为什么记忆化搜索写挂了,查不出错。0/30 pts code

Code

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
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 5005;

char s[MAXN];
int n, m, q;
vector<int> G0[MAXN], G1[MAXN], G[MAXN];
bool vis0[MAXN], vis1[MAXN];
bool vis2[MAXN], vis3[MAXN];
bool col[MAXN];
bool flag;
queue<pair<int, int> > qu;
bool vis[MAXN][MAXN], f[MAXN][MAXN];

void dfs_ck0(int u) {
vis1[u] = 1;
for (auto v : G0[u]) {
if (vis1[v] == 0) {
col[v] = col[u] ^ 1;
dfs_ck0(v);
} else if (col[v] == col[u]) flag = 0;
}
}

void dfs_gen0(int u) {
vis0[u] = 1;
for (auto v : G0[u])
if (vis0[v] == 0)
G[u].push_back(v), G[v].push_back(u), dfs_gen0(v);
}

void dfs_ck1(int u) {
vis3[u] = 1;
for (auto v : G1[u]) {
if (vis3[v] == 0) {
col[v] = col[u] ^ 1;
dfs_ck1(v);
} else if (col[v] == col[u]) flag = 0;
}
}

void dfs_gen1(int u) {
vis2[u] = 1;
for (auto v : G1[u])
if (vis2[v] == 0)
G[u].push_back(v), G[v].push_back(u), dfs_gen1(v);
}

int main() {
scanf("%d%d%d", &n, &m, &q);
scanf("%s", s + 1);
for (int i = 1; i <= m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
if (s[u] == s[v])
G0[u].push_back(v), G0[v].push_back(u);
else
G1[u].push_back(v), G1[v].push_back(u);
}
for (int i = 1; i <= n; ++i) {
if (vis0[i]) continue;
flag = 1;
dfs_ck0(i);
dfs_gen0(i);
if (flag == 0) G[i].push_back(i);
}
memset(col, 0, sizeof(col));
for (int i = 1; i <= n; ++i) {
if (vis2[i]) continue;
flag = 1;
dfs_ck1(i);
dfs_gen1(i);
if (flag == 0) G[i].push_back(i);
}
for (int i = 1; i <= n; ++i) {
qu.push(make_pair(i, i));
vis[i][i] = 1;
}
for (int u = 1; u <= n; ++u) {
for (auto v : G[u]) {
if (s[u] == s[v])
qu.push(make_pair(u, v)), vis[u][v] = vis[v][u] = 1;
}
}
while (qu.size()) {
int x = qu.front().first, y = qu.front().second;
qu.pop();
f[x][y] = f[y][x] = 1;
for (auto z : G[x]) {
for (auto w : G[y]) {
if (s[z] == s[w] && vis[z][w] == 0)
qu.push(make_pair(z, w)), vis[z][w] = vis[w][z] = 1;
}
}
}
for (int i = 1; i <= q; ++i) {
int a, b;
scanf("%d%d", &a, &b);
if (f[a][b]) puts("YES");
else puts("NO");
}
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×