NOIOL2022T2 讨论(思维)
NOIOL2022T2 讨论
解题思路
我们考虑什么时候不存在合法集合对。
两个集合不合法当且仅当他们有包含关系或无交集。我们考虑对集合排序,这样若 \(B⊆C,C⊆A\),那么有 \(B⊆A\)。这显然。所以排序可以简化计算。
我们从小到大枚举一遍,把现在所有扫过了的,包含元素 \(i\) 的最大集合标记到 \(i\) 上。每次我们检查一个新的集合,我们只需要验证是否对于 \(i\in S\),\(i\) 标记的集合的所有元素都在 \(S\) 中。这扫一遍即可验证。若存在一个标记不满足条件,那么这个集合和 \(S\) 就满足题目要求,输出即可。
我们的时间复杂度来到了 \(O(n\log n)\),瓶颈是排序。这个复杂度已经可以通过了。
但是我们可以用桶排序优化这个进程,复杂度来到 \(O(n+m)\) 。
代码
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
char read_char() {
char ch=getchar();
while(!isalpha(ch)) {
ch=getchar();
}
return ch;
}
const int MAXN=1e6+10;
int n;
int inc[MAXN],sz[MAXN],bel[MAXN];
vector<int> s[MAXN],buc[MAXN];
void work() {
for(int i=1;i<=n;i++) {
sz[i]=read();
for(int j=1;j<=sz[i];j++) {
s[i].push_back(read());
}
buc[sz[i]].push_back(i);
}
for(int i=1;i<=n;i++) {
int siz=buc[i].size();
for(int j=0;j<siz;j++) {
int u=buc[i][j];
for(int k=0;k<sz[u];k++) {
int x=s[u][k];
inc[bel[x]]++;
}
for(int k=0;k<sz[u];k++) {
int x=s[u][k];
if(bel[x]&&inc[bel[x]]!=sz[bel[x]]) {
puts("YES");
printf("%d %d\n",u,bel[x]);
return ;
}
}
for(int k=0;k<sz[u];k++) {
int x=s[u][k];
inc[bel[x]]--;
bel[x]=u;
}
}
}
puts("NO");
}
void clean() {
fill(sz+1,sz+n+1,0);
fill(inc+1,inc+n+1,0);
fill(bel+1,bel+n+1,0);
for(int i=1;i<=n;i++) {
buc[i].clear();
s[i].clear();
}
}
int main() {
int t=read();
while(t--) {
cin>>n;
work();
clean();
}
return 0;
}