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;
}
posted @ 2024-10-24 22:33  YipChip  阅读(30)  评论(0编辑  收藏  举报