[NOI online2022提高B] 讨论

题目描述
\(n\) 个人正在打模拟赛,模拟赛有 \(n\) 道题目。
有两人都会的题目并且没有人会的题目包含另一个人时,两者之间才会讨论。
(定义第 ii 个人会的题目的集合为 \(S_i\) ,即当 \(S_x\cap S_y\neq\varnothing\land S_x\not\subseteq S_y\land S_y\not\subseteq S_x\)时,第 \(x\) 人和第 \(y\) 人会讨论)
为了让模拟赛的效果更好,希望你可以找出一对会讨论的人或判断不存在。

输入格式
第一行一个正整数 \(T\) 表示数据组数,对于每组数据:
第一行一个正整数 \(n\) 表示人数和题目数量。
接下来 \(n\) 行,第 \(i\) 行第一个自然数 \(k_i\),表示第 \(i\) 个人会 \(k_i\)道题。接下来 \(k_i\)个正整数,每个数 \(x\) 表示第 \(i\) 个人会第 \(x\) 道题。

输出格式
对于每组数据:
如果没有会讨论的人,输出 NO
否则第一行输出 YES,第二行输出两个正整数 \(x\)\(y\),表示第 \(x\) 人和第 \(y\) 人会讨论。
如果有多种方案,输出任意一种即可。

输入输出样例
输入 #1

2
5
4 1 2 3 5
3 1 2 3
2 1 2
1 1
1 4
4
3 1 2 3
3 2 3 4
0
4 1 2 3 4

输出 #1

NO
YES
1 2

这题其实没有考任何的算法。简化题意,两个人会讨论,当且仅当他们有共同会的题目并且不存在一个人会的题目另一个人都会。

包含关系肯定是数量多的包含数量小的,所以我们把所有集合按大小从大到小排序后,如果我们就可以只需判断这个人会不会被之前的人包含就行了。

我们可以在一个数组上去判断两个人有没有共同会的题。也就是我们每看完一个人都在数组上把他会的题标上他的序号,对于一个人,我们枚举所有他会的题,看一下是否出现了不同的会的人。我们暂且把暂时没有人会也当作一个人。那么如果他的所有的会的题标的都是没有人,那么他没有和其他人有交集。如果都是一个人,那么他就被那个人包含了。如果出现了两个人,可以发现,他一定和集合数量小的那个人有讨论。整体其实没有考什么算法,只是单纯的用数组判断集合的关系罢了。

#include<cstdio>
const int N=5e5+5;
int n,q,a[N],b[N],st[N],rt[N],idx,tp,l,r;
struct node{
	int s,lc,rc;
}tr[N*40];
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
void insert(int o,int p,int l,int r,int x)
{
	if(l==r)
	{
		tr[o].s=tr[p].s+1;
		return;
	}
	int md=l+r>>1;
	if(md>=x)
	{
		tr[o].lc=++idx,tr[o].rc=tr[p].rc;
		insert(tr[o].lc,tr[p].lc,l,md,x);
	}
	else
	{
		tr[o].rc=++idx,tr[o].lc=tr[p].lc;
		insert(tr[o].rc,tr[p].rc,md+1,r,x);
	}
	tr[o].s=tr[tr[o].rc].s+tr[tr[o].lc].s;
}
int ask(int o,int l,int r,int x,int y)
{
	if(x<=l&&r<=y)
		return tr[o].s;
	int md=l+r>>1,ret=0;
	if(md>=x)
		ret+=ask(tr[o].lc,l,md,x,y);
	if(md<y)
		ret+=ask(tr[o].rc,md+1,r,x,y);
	return ret;
}
int main()
{
//	freopen("stack.in","r",stdin);
//	freopen("stack.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=n;i++)
		b[i]=read();
	for(int i=1;i<=n;i++)
	{
		while(tp&&(a[st[tp]]==a[i]||b[st[tp]]<=b[i]))
			--tp;
		rt[i]=++idx;
		insert(rt[i],rt[i-1],0,n,st[tp]);
		st[++tp]=i;
	}
	while(q--)
	{
		l=read(),r=read();
		printf("%d\n",ask(rt[r],0,n,0,l-1)-ask(rt[l-1],0,n,0,l-1));
	}
	return 0;
}
posted @ 2022-04-13 22:11  灰鲭鲨  阅读(59)  评论(0编辑  收藏  举报