C+K+S
这个题还真挺不错的,值得补出来!
我们可以钦定任意一点的颜色(取值为 ),然后沿着边的方向对其他点染色。具体地,第设 个点的颜色为 ,且有一条边 ,则 。这个图上的所有环长度都是 的倍数,因此一定可以按照上述规则对该图染色。
一个图有且仅有 种染色方式,构造出一组 后可以通过 构造出来所有其他的染色方式。
我们在加边的时候遵循相同的规则,从颜色 连接到 ,这样构造出来的图肯定也能满足所有的环为 的倍数。
一种暴力做法是,枚举两个图的所有染色形式,检查:对于每种颜色 ,图一中颜色为 的 outgoing 点数量是否等于图二颜色为 的 incoming 点数量,且图而中颜色为 的 outgoing 点数量是否等于图一颜色为 的 incoming 点数量。其时间复杂度为 。两个图同时偏移相同的数时,效果相同。所以,我们只需要枚举某一个图的染色方式即可。这样的复杂度为 。
观察一下统计每种颜色 incoming 点数量与 outgoing 点数量的数组,所有点颜色增加 相当于数组循环移位一次。而我们只需要判两个数组是否相等,上哈希搞一下就好了。
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iostream>
#include <queue>
#include <random>
#include <ranges>
#include <vector>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using ulli = __uint128_t;
const ui W = [] {
random_device rd;
return uniform_int_distribution<ui>{(ui)2e5, (ui)2e6}(rd);
}();
constexpr uli P = 1000000000000001771;
auto const H = []() {
array<uli, (size_t)2e5 + 1> h;
h[0] = 1;
for (size_t i = 1; i < h.size(); ++i) h[i] = h[i - 1] * (ulli)W % P;
return h;
}();
vector<size_t> color(size_t k, vector<vector<size_t>>& mp, size_t s = 0,
size_t c = 0) {
vector<size_t> a(mp.size(), ~0);
a[s] = c;
queue<size_t> q{{s}};
while (!q.empty()) {
size_t p = q.front();
q.pop();
ui t = (a[p] + 1) % k;
for (size_t i : mp[p])
if (~a[i])
assert(a[i] == t);
else
a[i] = t, q.push(i);
}
return a;
}
int main(void) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
size_t T;
fin >> T;
while (T--) {
size_t n, k, m0, m1;
fin >> n >> k;
vector<bool> a0(n), a1(n);
vector<vector<size_t>> g0(n), g1(n);
for (auto i : a0) {
char c;
fin >> c;
i = c == '1';
}
fin >> m0;
while (m0--) {
size_t x, y;
fin >> x >> y;
g0[--x].emplace_back(--y);
}
for (auto i : a1) {
char c;
fin >> c;
i = c == '1';
}
fin >> m1;
while (m1--) {
size_t x, y;
fin >> x >> y;
g1[--x].emplace_back(--y);
}
if ((count(a0.begin(), a0.end(), true) == n &&
count(a1.begin(), a1.end(), false) == n) ||
(count(a0.begin(), a0.end(), true) == 0 &&
count(a1.begin(), a1.end(), false) == 0)) {
fout << "YES\n";
continue;
}
auto t0 = color(k, g0), t1 = color(k, g1);
vector<size_t> c0i(k), c0o(k), c1i(k), c1o(k);
for (size_t i : ranges::views::iota((size_t)0, n))
++(!a0[i] ? c0i : c0o)[t0[i]], ++(!a1[i] ? c1i : c1o)[t1[i]];
uli h0i = 0, h0o = 0, h1i = 0, h1o = 0;
for (size_t i : ranges::views::iota((size_t)0, k))
h0i = (h0i + (ulli)c0i[i] * H[k - i - 1]) % P,
h0o = (h0o + (ulli)c0o[i] * H[k - i - 1]) % P,
h1i = (h1i + (ulli)c1i[i] * H[k - i - 1]) % P,
h1o = (h1o + (ulli)c1o[i] * H[k - i - 1]) % P;
auto shift = [k](uli v, size_t c) -> uli {
return ((v + P - (ulli)c * H[k - 1] % P) * W + (ulli)c * H[0]) % P;
};
for (size_t i : ranges::views::iota((size_t)0, k)) {
if (h0o == shift(h1i, c1i[i]) && h1o == shift(h0i, c0i[0])) {
fout << "YES\n";
goto end;
}
h1i = shift(h1i, c1i[i]), h1o = shift(h1o, c1o[i]);
}
fout << "NO\n";
end:;
}
return 0;
}