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;
}