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

posted @ 2022-04-02 17:25  huayucaiji  阅读(32)  评论(0编辑  收藏  举报