洛谷P5361热闹的聚会与尴尬的聚会

Posted stoorz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P5361热闹的聚会与尴尬的聚会相关的知识,希望对你有一定的参考价值。

题目

题目链接:https://www.luogu.com.cn/problem/P5361
小 Q 的生日快到了,他决定周末邀请一些朋友到他的新房子一起聚会!
他的联系薄上有 \\(n\\) 位好友,他们两两之间或者互相认识,或者互相不认识。小 Q 希望在周六办一个热闹的聚会,再在周日办一个尴尬的聚会。

  • 一场热闹度为 \\(p\\) 的聚会请来了任意多位好友,对于每一位到场的好友来说都有至少 \\(p\\) 位他认识的好友也参加了聚会,且至少对于一位到场的好友来说现场恰好有 \\(p\\) 位他认识的好友;
  • 一场尴尬度为 \\(q\\) 的聚会请来了恰好 \\(q\\) 位好友,且他们两两互不认识。

两场聚会可能有重复的参与者,联系薄上也有可能有某些好友同时缺席了两场聚会。
小 Q 喜欢周六聚会的热闹度 \\(p\\) 与周日聚会的尴尬度 \\(q\\) 之间满足:\\(\\left\\lfloor \\frac{n}{p+1} \\right\\rfloor\\! \\le q\\)\\(\\left\\lfloor \\frac{n}{q+1} \\right\\rfloor\\! \\le p\\)
请帮助小 Q 找出一个可行的邀请方案。
\\(Q\\leq 32,n\\leq 10^4,m\\leq 10^5\\)

思路

也就是给你一张图,要求选出两个点集(可以有交):

  • 第一个点集没有限制,单独把第一个点集里的点和边拿出来,记度数最小的点的度数为 \\(p\\)
  • 第二个点集要求是图的独立集,记点集大小为 \\(q\\)

要求 \\(\\left\\lfloor \\frac{n}{p+1} \\right\\rfloor\\! \\le q\\)\\(\\left\\lfloor \\frac{n}{q+1} \\right\\rfloor\\! \\le p\\),并给出任意一种方案。
看到独立集自然想到这是一道乱搞题。
假设目前第一个点集为 \\(S\\),我们要删去一个点使得 \\(p\\) 更大,那么显然删去度数最小的点最优。然后继续更新其他依然在 \\(S\\) 中的点的度数。
维护一个堆即可。不难发现这样是可以找到最优的 \\(p\\) 的。
考虑最大化 \\(q\\),显然这个是没有确定性算法的,所以直接模拟退火!
其实可以直接不断选择度数最小的点,删去图中与它相连的边。
因为我们每次选择的点的度数一定不超过 \\(p\\),如果超过了,那么第一问中肯定有大于 \\(p\\) 的方案。所以每次最多删去 \\(p+1\\) 个点,那么就有 \\(\\left\\lfloor \\frac{n}{p+1} \\right\\rfloor\\! \\le q\\)
时间复杂度 \\(O(Q(n+m)\\log n)\\)

代码

#include <bits/stdc++.h>
#define mp make_pair
#define ST first
#define ND second
using namespace std;

const int N=200010;
int Q,n,m,p,pos,tot,head[N],deg[N],deg1[N],vis1[N];
bool vis2[N];
priority_queue<pair<int,int> > q;
queue<int> q1;

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot; deg[to]++; deg1[to]++;
}

void prework()
{
	while (q.size()) q.pop();
	while (q1.size()) q1.pop();
	for (int i=1;i<=n;i++)
		head[i]=-1,deg[i]=deg1[i]=vis1[i]=vis2[i]=0;
	tot=p=pos=0;
}

int main()
{
	Q=read();
	while (Q--)
	{
		n=read(); m=read();
		prework();
		for (int i=1,x,y;i<=m;i++)
		{
			x=read(); y=read();
			add(x,y); add(y,x);
		}
		for (int i=1;i<=n;i++)
			q.push(mp(-deg[i],i));
		for (int i=1;i<=n;i++)
		{
			while (vis1[q.top().ND]) q.pop();
			int x=q.top().ND; q.pop();
			vis1[x]=i;
			if (deg[x]>=p) p=deg[x],pos=i;
			for (int i=head[x];~i;i=e[i].next)
			{
				int v=e[i].to;
				if (!vis1[v]) q.push(mp(-(--deg[v]),v));
			}
		}
		while (q.size()) q.pop();
		for (int i=1;i<=n;i++)
			deg[i]=deg1[i],q.push(mp(-deg[i],i));
		while (q.size())
		{
			while (q.size() && vis2[q.top().ND]) q.pop();
			if (!q.size()) break;
			int x=q.top().ND; q.pop();
			vis2[x]=1; q1.push(x);
			for (int i=head[x];~i;i=e[i].next)
				vis2[e[i].to]=1;
		}
		tot=0;
		for (int i=1;i<=n;i++)
			if (vis1[i]>=pos) tot++;
		cout<<tot;
		for (int i=1;i<=n;i++)
			if (vis1[i]>=pos) cout<<\' \'<<i;
		cout<<"\\n";
		cout<<q1.size();
		for (;q1.size();q1.pop())
			cout<<\' \'<<q1.front();
		cout<<"\\n";
	}
	return 0;
}

以上是关于洛谷P5361热闹的聚会与尴尬的聚会的主要内容,如果未能解决你的问题,请参考以下文章

vijos2054 SDOI2019 热闹的聚会与尴尬的聚会

[SDOI2019] 热闹又尴尬的聚会

[SDOI2019]热闹又尴尬的聚会(图论+set+构造)

洛谷 P1293 班级聚会

国庆回家,同学聚会程序员的尴尬

聚会的快乐