题解 P8252 [NOI Online 2022 提高组] 讨论

先把所有集合按大小排序,设排序后的为新编号。

考虑所有会做第 \(i\) 题的人,如果存在两个人 \(x,y\) 满足 \(x\) 的新编号小于 \(y\) 且存在一道题 \(x\)\(y\) 不会,\((x,y)\) 就是答案。否则就不存在。

考虑不存在的情况,一定形成了一条包含关系的链。所以条件可以转化为考虑 \(x,y\) 满足 \(y\)\(x\) 的后继就可以了。

如果再某一题中 \(x\) 的后继为 \(y\),而在另一题中 \(x\) 的后继为 \(z\)(或者没有后继)。那么只要比较 \(y,z\) 新编号的大小,会发现 \((x,y)\) 或者 \((x,z)\) 一定满足条件了。

否则每道题 \(x\) 的后继都是一样的,肯定找不到答案了,因此不存在。

时间复杂度 \(O(n\log n)\),瓶颈在排序。可以换用桶排变为 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;
const int N=2000005;
int n;
vector<int>v[N];
int a[N],ans[N];
bool cmp(int x,int y)
{
	if (a[x]==a[y]) return x<y;
	return a[x]<a[y];
}

inline int read()
{
    int F=1,ANS=0;
	char C=getchar();
    while (C<'0'||C>'9')
	{
		if (C=='-') F=-1;
		C=getchar();
	}
    while (C>='0'&&C<='9')
	{
		ANS=ANS*10+C-'0';
		C=getchar();
	}
    return F*ANS;
}

void work()
{
	n=read();
	for (int i=1;i<=n;i++) v[i].clear();
	for (int i=1;i<=n;i++) ans[i]=0;
	for (int i=1;i<=n;i++) 
	{
		a[i]=read();
		for (int j=1;j<=a[i];j++)
		{
			int x=read();
			v[x].push_back(i);
		}
	}
	a[n+1]=n+1;
	for (int i=1;i<=n;i++) v[i].push_back(n+1);
	for (int i=1;i<=n;i++) sort(v[i].begin(),v[i].end(),cmp);
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<(int)v[i].size();j++)
		{
			if (ans[v[i][j-1]]==0) ans[v[i][j-1]]=v[i][j];
			else if (ans[v[i][j-1]]!=v[i][j])
			{
				int res=ans[v[i][j-1]];
				if (cmp(v[i][j],res)) res=v[i][j];
				printf("YES\n%d %d\n",v[i][j-1],res);
				return;
			}
		}
	}
	printf("NO\n");
}

int main()
{
	int T=read();
	while (T--) work();
	return 0;
}

posted @ 2022-04-03 19:53  Little09  阅读(57)  评论(0编辑  收藏  举报