17 LCA模拟赛1T2 剧院始于演员 题解

剧院始于演员

题面

\(n\) 个演员,共 \(m\) 场演出,每场演出会给出这场演出的演员名单,共 \(k_i\) 个姓名

对于每个演员,求最早在哪一场演出结束后能够确定其对应姓名?

\(1 \le n , m \le 10^5, \sum k_i \le 10^5\)

题解

可以发现,两个演员能够区分当且仅当某场演出中,一个演员出现,一个演员不出现

所以想要让某个演员和其他演员都区分开,那这个演员在每场的演出情况一定和其他演员不同,所以我们可以记录每个演员在前面场次的出现情况,记录一个状态

如果某个演员的状态和其他演员都不同,那么就能够确定这个演员的姓名

考虑如何实现?

我们可以对状态进行哈希,以区分出现和不出现

对每个哈希值,我们需要知道其对应了多少个演员,并且要快速的插入演员,删除演员,所以可以对每个哈希值维护一个 set ,用 map 来维护 哈希值与 set 的对应关系

但还没完,在每场演出结束后,我们还需要快速获取到大小为 1 的演员集合,并将对应的哈希值从 map 里删除,我们可以用另一个 set 来维护 集合大小和对应哈希值

那么这道题就做完了,时间复杂度 \(O(n \log n)\)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <random>
#include <ctime>
#include <unordered_map>
#include <set>

using namespace std;

typedef unsigned long long ull;

void close_stream () {
    ios :: sync_with_stdio (0);
    cin.tie (0);
    cout.tie (0);
}

namespace solution_b {

    const int N = 1e5 + 10;

    int n, m, ans[N];
    ull hash[N];
    unordered_map <ull, set <int> > mp;
    set <pair <int, ull> > st;
    mt19937_64 rng (time (0));

    void solve () {
        close_stream ();
        cin >> n >> m;
        st.emplace (n, 0);
        for (int i = 1; i <= n; i ++) {
            mp[0].emplace (i);
        }
        for (int i = 1; i <= m; i ++) {
            int cnt, x;
            cin >> cnt;
            ull tmp = rng ();
            for (int j = 1; j <= cnt; j ++) {
                cin >> x;
                if (ans[x]) continue;

                // 取出当前演员
                st.erase ({mp[hash[x]].size (), hash[x]});
                mp[hash[x]].erase (x);
                if (mp[hash[x]].size () == 0) mp.erase (hash[x]);
                else st.emplace (mp[hash[x]].size (), hash[x]);

                hash[x] ^= tmp;

                // 再将其插入
                st.erase ({mp[hash[x]].size (), hash[x]});
                mp[hash[x]].emplace (x);
                st.emplace (mp[hash[x]].size (), hash[x]);
            }

            for (auto it = st.begin (); it != st.end (); it ++) {
                int siz = it->first;
                ull val = it->second;
                if (siz > 1) break;
                ans[*mp[val].begin ()] = i;
                mp.erase (val);                
            }

            while (st.size () && st.begin()->first == 1) {
                st.erase (st.begin ());
            }
        }

        for (int i = 1; i <= n; i ++) {
            cout << ans[i] << ' ';
        }
        cout << '\n';
    }
}

int main () {

    solution_b :: solve ();

    return 0;
}
posted @ 2025-10-05 17:34  michaele  阅读(2)  评论(0)    收藏  举报