P7234 [JSOI2014] 歌剧表演
unputdownable · · 题解
首先考虑如何区分两个人(假设只有两个人)。
不难发现对于任意两个人
这启发我们可以维护若干个集合,每个集合中的人参加的表演集合相同。
如果一个集合大小为
接下来考虑如何维护上文所述集合。
首先,你无需维护参加的表演集合,因为我们只需要知道有哪些(人的)集合,而不需知道他们参加过那些表演。
这样初始时所有人都在集合
每进行一场表演,就相当于更改一些人的表演集合,此时有些集合会分裂,注意到分裂的时候只会分裂参加这场表演的人。
于是对于每场表演,在参演人员中找到在同一个集合里的,直接分裂成一个新集合,考虑到此时只有原集合以及新集合的大小可能为
注意到分裂出一个新集合至少需要一个人参加表演,所以集合数目至多为
这样就可以把一场表演的复杂度降到
具体细节可以看代码。
Code:
int n,m,k;
int tag[100005],xorsum[200005],num[200005],cnttag;
// tag 表示这个人在哪个集合
// xorsum 是一个集合里所有人编号的异或和,用于在集合大小为 1 的时候找到这个人。
int Ans[100005];
pii tmp[100005]; int cnt;
int checklist[100005],cntchecklist;
signed main() {
n=read(); m=read(); tmp[0]=make_pair(-1,-1);
for(int i=1; i<=n; ++i) xorsum[0]^=i; num[0]=n;
for(int T=1; T<=m; ++T) {
k=read(); cnt=0; cntchecklist=0;
for(int i=1,x; i<=k; ++i) {
x=read();
if(!Ans[x]) tmp[++cnt]=make_pair(tag[x],x);
}
sort(tmp+1,tmp+cnt+1);
for(int i=1; i<=cnt; ++i) {
if(tmp[i].first!=tmp[i-1].first) {
tag[tmp[i].second]=++cnttag;
checklist[++cntchecklist]=tmp[i].first;
checklist[++cntchecklist]=cnttag;
} else tag[tmp[i].second]=cnttag;
--num[tmp[i].first]; xorsum[tmp[i].first]^=tmp[i].second;
++num[cnttag]; xorsum[cnttag]^=tmp[i].second;
}
for(int i=1; i<=cntchecklist; ++i) if(num[checklist[i]]==1) Ans[xorsum[checklist[i]]]=T;
}
for(int i=1; i<=n; ++i) write(Ans[i]),putchar(" \n"[i==n]);
return 0;
}