CF2023C C+K+S 题解
题面
给您两个强联通的 \(^{\dagger}\) 有向图,每个图都有精确的 \(n\) 个顶点,但可能有不同数量的边。仔细观察后,您发现了一个重要特征——这些图中任何一个环的长度都能被 \(k\) 除尽。
每个 \(2n\) 顶点都属于两种类型中的一种:传入或传出。每个顶点的类型都是已知的。
您需要确定是否有可能在两张图之间建立恰好 \(n\) 条有向边,从而满足以下四个条件:
- 任何添加的边的两端都位于不同的图中。
- 从每个传出顶点,正好有一条新增边传出。
- 从每个传入顶点,正好有一条新增边进入。
- 在生成的图中,任何环的长度都能被 \(k\) 整除。
\(^{\dagger}\) 强联通图是指从每个顶点到其他顶点都有一条路径的图。
题解
由于所有环的长度被 \(k\) 整除,我们可以对上面进行环染色,染色后两个图的每一个点一定是唯一对应一种颜色,同时也可以是这个颜色的轮换。
因为图是强连通的,所以两个图任意找一点跑 \(\mathtt{bfs}\) 即可染色,考虑如何连接两个图,如果两个度出入度不等,一定不存在,否则,如果一个图全部都是出点或入点,那么这个图一定存在,因为加边不会构成环。
接下来考虑剩余的情况如何求解,考虑把第一张图所有颜色为 \(k\) 的出点个数记录下来给 \(s[k+1]\),入点个数记录给 \(s[k-1]\),第二张图必定满足记录的数据可以通过轮换与 \(s\) 相同,考虑 \(\mathtt{KMP}\) 匹配即可,为了防止 \(\mathtt{KMP}\) 匹配产生混淆,给入点加 \(n\),出点加 \(1\) 可以保证结果一定不同。
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
int n, m, k, a[N], b[N];
void solve()
{
int n, m, k;
cin >> n >> k;
vector<int> a(n), b(n);
vector<vector<int>> ea(n), eb(n);
for (int i = 0; i < n; i ++ ) cin >> a[i];
cin >> m;
for (int i = 1; i <= m; i ++ )
{
int x, y;
cin >> x >> y;
x -- , y -- ;
ea[x].push_back(y);
}
for (int i = 0; i < n; i ++ ) cin >> b[i];
cin >> m;
for (int i = 0; i < m; i ++ )
{
int x, y;
cin >> x >> y;
x -- , y -- ;
eb[x].push_back(y);
}
int cnt0 = count(a.begin(), a.end(), 0);
int cnt1 = count(b.begin(), b.end(), 1);
if (cnt0 != cnt1) return cout << "NO\n", void();
if (cnt0 == n || !cnt0) return cout << "YES\n", void();
vector<int> c1(n, -1), c2(n, -1);
queue<int> q;
q.push(0), c1[0] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < ea[u].size(); i ++ )
{
int j = ea[u][i];
if (c1[j] == -1)
{
c1[j] = (c1[u] + 1) % k;
q.push(j);
}
}
}
q.push(0), c2[0] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < eb[u].size(); i ++ )
{
int j = eb[u][i];
if (c2[j] == -1)
{
c2[j] = (c2[u] + 1) % k;
q.push(j);
}
}
}
vector<ll> s(k + 1), t(k + 1);
for (int i = 0; i < n; i ++ )
{
if (a[i]) s[(c1[i] + 1) % k + 1] += n;
else s[(c1[i] - 1 + k) % k + 1] += 1;
if (b[i]) t[c2[i] + 1] += 1;
else t[c2[i] + 1] += n;
}
for (int i = 1; i <= k; i ++ ) t.push_back(t[i]);
vector<int> ne(k * 2 + 1);
for (int i = 2, j = 0; i < s.size(); i ++ )
{
while (j && s[i] != s[j + 1]) j = ne[j];
if (s[i] == s[j + 1]) j ++ ;
ne[i] = j;
}
for (int i = 1, j = 0; i < t.size(); i ++ )
{
while (j && t[i] != s[j + 1]) j = ne[j];
if (t[i] == s[j + 1]) j ++ ;
if (j == k) return cout << "YES\n", void();
}
cout << "NO\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin >> T;
while (T -- ) solve();
return 0;
}