给定 n n n个点, m m m条边的图。每个点有 0 / 1 0/1 0/1的标号,有 Q Q Q个询问,每次询问点对 ( u , v ) (u,v) (u,v)间是否一条路径(不一定是简单路径),满足路径经过的点的标号所形成的串是回文串。
n ≤ 5000 , m ≤ 5000000 n \leq 5000, m \leq 5000000 n≤5000,m≤5000000
设 f i , j f_{i,j} fi,j表示 i , j i,j i,j是否存在一条满足条件的路径。初始状态和转移显然。
考虑每次转移的复杂度太高,而这张图的点数较少,边数较多。
把连接了同一种标号的边拎出来。可以发现这些边使图形成了若干连通块。对于某个连通块,如果它是二分图,那么进行黑白染色后,同色点之间路径的长度只可能为奇数,异色点之间路径长度只可能为偶数。
不难发现,把这张二分图替换为它的一棵生成树,上述性质不变,而一个端点在这个连通块的状态的转移也不变,因为经过原图上的一条边可以替代为经过这条非树边在树上的路径(让另一个端点重复走即可)。而如果这不是二分图,意味着可以可以经过某个环改变路径的奇偶性,所以给这棵生成树加上一个自环。
对于连接不同标号的边同理。经过这样操作之后,边数不超过 2 n − 2 2n-2 2n−2,直接套用上述暴力 D P DP DP即可。
#include
using namespace std;
const int maxn = 5005, maxm = 500005;
int n, m, q;
vector<pair<int, int>> E[2][2];
int fa[maxn], col[maxn], f[maxn][maxn];
vector<int> to[maxn][2];
char s[maxn];
queue<pair<int, int>> que;
struct edge
{
int to, next;
} e[maxm * 4];
int h[maxn], tot;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
inline void add(int u, int v)
{
e[++tot] = (edge) {v, h[u]}; h[u] = tot;
e[++tot] = (edge) {u, h[v]}; h[v] = tot;
to[u][s[v] - '0'].push_back(v);
to[v][s[u] - '0'].push_back(u);
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
bool merge(int x, int y)
{
x = find(x); y = find(y);
if (x == y) return 0;
return fa[x] = y, 1;
}
void dfs(int u, int fa)
{
for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
if (v != fa) col[v] = col[u] ^ 1, dfs(v, u);
}
void solve(int c1, int c2)
{
for (int i = 1; i <= n; ++i) h[i] = 0, fa[i] = i;
tot = 0;
for (auto e : E[c1][c2]) {
int x = e.first, y = e.second;
if (merge(x, y)) add(x, y);
}
for (int i = 1; i <= n; ++i)
if (fa[i] == i) col[i] = 0, dfs(i, 0);
for (auto e : E[c1][c2]) {
int x = e.first, y = e.second;
if (col[x] == col[y]) col[find(x)] = -1;
}
for (int i = 1; i <= n; ++i)
if (col[i] == -1) add(i, i);
}
void bfs()
{
for (int i = 1; i <= n; ++i) f[i][i] = 1, que.push(make_pair(i, i));
for (int i = 1; i <= n; ++i)
for (int j : to[i][s[i] - '0']) if (i < j) f[i][j] = 1, que.push(make_pair(i, j));
int u, v, siz1, siz2, i, j, a, b;
while (!que.empty()) {
u = que.front().first, v = que.front().second;
que.pop();
siz1 = to[u][0].size(); siz2 = to[v][0].size();
for (i = 0; i < siz1; ++i)
for (j = 0; j < siz2; ++j) {
a = to[u][0][i], b = to[v][0][j];
if (a > b) swap(a, b);
if (f[a][b]) continue;
que.push(make_pair(a, b));
f[a][b] = 1;
}
siz1 = to[u][1].size(); siz2 = to[v][1].size();
for (i = 0; i < siz1; ++i)
for (j = 0; j < siz2; ++j) {
a = to[u][1][i], b = to[v][1][j];
if (a > b) swap(a, b);
if (f[a][b]) continue;
que.push(make_pair(a, b));
f[a][b] = 1;
}
}
}
int main()
{
n = gi(); m = gi(); q = gi();
scanf("%s", s + 1);
for (int u, v, i = 1; i <= m; ++i) {
u = gi(); v = gi();
if (s[u] > s[v]) swap(u, v);
E[s[u] - '0'][s[v] - '0'].push_back(make_pair(u, v));
}
solve(0, 0);
solve(1, 1);
solve(0, 1);
bfs();
for (int a, b, i = 1; i <= q; ++i) {
a = gi(); b = gi();
if (a > b) swap(a, b);
puts(f[a][b] ? "YES" : "NO");
}
return 0;
}