Luogu P8819 [CSP-S 2022] 星战 题解 [ 蓝 ] [ 基环树 ] [ 哈希 ]

星战:唐题,感觉 *2100 顶天了。

容易将题意转化为:维护一个数据结构,支持删除 / 添加有向图中以 \(u\) 为出点的所有边,删除 / 添加某一条单独的边,查询有向图是否为内向基环树森林

考虑内向基环树森林的充分必要条件:

  • 有向图中的边数为 \(\bm n\)
  • 有向图中每个节点的出度都是 \(1\)

维护边数是简单的,修改的时候多记录几个值即可。考虑如何判断出度是否都为 \(1\),因为第一个条件已经保证了总边数为 \(n\),所以可以直接 Xor Hashing,给每个节点随机赋权,当前有向图的哈希值即为所有边的入点的哈希异或和。内向基环树森林的哈希值一定是所有点的哈希值的异或和

该做法的正确性是基于恰好有 \(\bm n\) 条边的条件而言的,因为有了 \(n\) 条边,所以一旦有一个节点有两条出边,则一定对应着另一个节点一条出边都没有,此时该点不会被计入哈希值中,自然能被判断出来。

当同时满足边数为 \(n\)、哈希值相等时,说明是内向基环树森林。直接维护就能做到时间复杂度 \(O(n + m + q)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
mt19937_64 rnd((unsigned) time(NULL));
ll rd(ll l, ll r)
{
    return uniform_int_distribution<ll> (l, r) (rnd);
}
const int N = 500005;
const ll MXV = 1e18;
int n, m, q, nowe;
ll w[N], smhs, orihs[N], nowhs[N], cur, lste[N], orie[N];
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        w[i] = rd(1, MXV);
        smhs ^= w[i];
    }
    for(int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        nowe++;
        orihs[v] ^= w[u];
        nowhs[v] ^= w[u];
        lste[v]++;
        orie[v]++;
        cur ^= w[u];
    }
    cin >> q;
    while(q--)
    {
        int op, u, v;
        cin >> op;
        if(op == 1)
        {
            cin >> u >> v;
            nowhs[v] ^= w[u];
            cur ^= w[u];
            nowe--;
            lste[v]--;
        }
        else if(op == 2)
        {
            cin >> u;
            cur ^= nowhs[u];
            nowhs[u] = 0;
            nowe -= lste[u];
            lste[u] = 0;
        }
        else if(op == 3)
        {
            cin >> u >> v;
            nowhs[v] ^= w[u];
            cur ^= w[u];
            nowe++;
            lste[v]++;
        }
        else
        {
            cin >> u;
            cur ^= (nowhs[u] ^ orihs[u]);
            nowhs[u] = orihs[u];
            nowe += orie[u] - lste[u];
            lste[u] = orie[u];
        }
        if(nowe == n && cur == smhs) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}
posted @ 2025-10-15 21:09  KS_Fszha  阅读(9)  评论(0)    收藏  举报