【SDOI 2019】热闹又尴尬的聚会

传送门


problem

Day2 T1

小Q的联系薄上有 n n n 位好友,他们两两之间或者互相认识,或者互相不认识。小Q希望在周六办一个热闹的聚会,再在周日办一个尴尬的聚会。

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

两场聚会可能有重复的参与者,联系薄上也有可能有某些好友同时缺席了两场聚会。小Q喜欢周六聚会的热闹度pp与周日聚会的尴尬度qq之间满足: ⌊ n p + 1 ⌋ ≤ q \lfloor \frac{n}{p+1} \rfloor\le q p+1nq ⌊ n q + 1 ⌋ ≤ p \lfloor \frac{n}{q+1} \rfloor\le p q+1np

请帮助小Q找出一个可行的邀请方案。


solution

有一个比较乱搞的方法,可以看这篇博客

这道题真的是神仙构造题啊。

我们先考虑把约束 ⌊ n p + 1 ⌋ ≤ q \lfloor \frac{n}{p+1} \rfloor\le q p+1nq 化简。由于 ⌊ n x ⌋ = ⌈ n − x + 1 x ⌉ \lfloor \frac{n}{x} \rfloor=\lceil \frac{n-x+1}{x} \rceil xn=xnx+1,我们有:

⌈ n − ( p + 1 ) + 1 p + 1 ⌉ ≤ q \lceil \frac{n-(p+1)+1}{p+1} \rceil\le q p+1n(p+1)+1q

那把上取整去掉,原式显然也成立,即:

n − p p + 1 ≤ q \frac{n-p}{p+1}\le q p+1npq

然后乘过去得到 n ≤ p + q + p q n\le p+q+pq np+q+pq,即 n &lt; p + q + p q + 1 = ( p + 1 ) ( q + 1 ) n&lt;p+q+pq+1=(p+1)(q+1) n<p+q+pq+1=(p+1)(q+1)

显然 ⌊ n q + 1 ⌋ ≤ p ⌊\frac{n}{q+1}⌋≤p q+1np 也能推出一样的不等式。

也就是说,我们只要满足在 p , q p,q p,q 各自满足条件时,再满足 ( p + 1 ) ( q + 1 ) &gt; n (p+1)(q+1)&gt;n (p+1)(q+1)>n 就行了。

我们每次从图上选出度数最小的点,记录它的度数 d e g i deg_i degi删除相邻的 d e g i deg_i degi 个点,如此反复至无点可选,设进行了 q q q 次,显然有:

∑ i = 1 q ( d e g i + 1 ) = n ∑_{i=1}^q(deg_i+1)=n i=1q(degi+1)=n

热闹的聚会:存在一个热闹度 p = max ⁡ i = 1 q d e g i p=\max\limits_{i=1}^q deg_i p=i=1maxqdegi 的方案。我们设在点 x x x 取到 max ⁡ i = 1 q d e g i \max\limits_{i=1}^q deg_i i=1maxqdegi,此时与 x x x 相连的未被删除的点的度数必然 ≥ max ⁡ i = 1 q d e g i ≥\max\limits_{i=1}^q deg_i i=1maxqdegi,那么方案就是与 x x x x x x 相邻的这些点 相邻且未被删除的所有点。

尴尬的聚会:显然选出来的这 q q q 个点可以参加尴尬的聚会。

而且,此时有

( max ⁡ i = 1 q d e g i + 1 ) q ≥ ∑ i = 1 q ( d e g i + 1 ) = n ⇒ ( p + 1 ) ( q + 1 ) &gt; n (\max\limits_{i=1}^q deg_i+1)q≥∑_{i=1}^q(deg_i+1)=n\Rightarrow(p+1)(q+1)&gt;n (i=1maxqdegi+1)qi=1q(degi+1)=n(p+1)(q+1)>n

是满足约束的。


code

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 10005
#define M 100005
using namespace std;
int n,m,t;
int deg[N],Del[N],vis[N];
int first[N],v[M<<1],nxt[M<<1];
vector<int>A,B;
void add(int x,int y){
	nxt[++t]=first[x],first[x]=t,v[t]=y;
}
void Clear(){
	t=0,A.clear(),B.clear();
	memset(deg,0,sizeof(deg));
	memset(Del,0,sizeof(Del));
	memset(first,0,sizeof(first));
}
void solve(){
	int Min=n,Max=0,tot=0,pos,now;
	for(int i=1;i<=n;++i)  if(Min>deg[i])  Min=deg[i],pos=i;
	memset(vis,0,sizeof(vis));
	while(Min<=n){
		int x=pos;
		B.push_back(x),Del[x]=++tot;
		if(Max<deg[x])  Max=deg[x],now=tot;
		for(int i=first[x];i;i=nxt[i]){
			int to=v[i];
			if(!Del[to]){
				Del[to]=tot;
				for(int j=first[to];j;j=nxt[j])  deg[v[j]]--;
			}
		}
		Min=n+1;
		for(int i=1;i<=n;++i)  if(Min>deg[i]&&!Del[i])  Min=deg[i],pos=i;
	}
	for(int i=1;i<=n;++i)  if(Del[i]>=now)  A.push_back(i);
	printf("%d ",A.size());
	for(int i=0;i<A.size();++i)  printf("%d ",A[i]);puts("");
	printf("%d ",B.size());
	for(int i=0;i<B.size();++i)  printf("%d ",B[i]);puts("");
}
int main(){
	int T,x,y;
	scanf("%d",&T);
	while(T--){
		Clear();
		scanf("%d%d",&n,&m);
		while(m--){
			scanf("%d%d",&x,&y);
			add(x,y),add(y,x),deg[x]++,deg[y]++;
		}
		solve();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值