【题解】LuoGu5361:[SDOI2019]热闹又尴尬的聚会

本文介绍了一种解决构造题的算法思路,通过在图中选择度数最小的点并删除与其直接相连的点,重复此过程直到图为空。最终选取度数最大时刻的图作为解的一部分,证明了这种方法可以满足题目要求的条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题传送门
构造题
首先对于条件进行转化–> ( p + 1 ) ( q + 1 ) > n (p+1)(q+1)>n (p+1)(q+1)>n
然后就是构造一个 p , q p,q p,q出来

考虑一种做法:

  • 在图中选出度数最小的点,这个度数记为 d i d_i di
  • 将该点以及与该点直接相连的点删去
  • 重复上述过程直到无点为止
  • 取出 d i d_i di最大的时刻的图作为第一个点集,此时热闹度 p = d i p=d_i p=di,取所有操作1选择的点作为第二个点集就行了

考虑证明正确性:
设操作1共进行了 q q q次, d m a x = m a x ( d i ) dmax=max(d_i) dmax=max(di)
得到 ∑ i = 1 q ( d i + 1 ) = n \sum_{i=1}^{q}(d_i+1)=n i=1q(di+1)=n, p = d m a x p=dmax p=dmax
那么 ( p + 1 ) q > = ∑ i = 1 q ( d i + 1 ) = n (p+1)q>=\sum_{i=1}^{q}(d_i+1)=n (p+1)q>=i=1q(di+1)=n
( p + 1 ) ( q + 1 ) > n (p+1)(q+1)>n (p+1)(q+1)>n
得证

至于怎么维护这个删点并且修改点集中度数,每次还要找最小,用 s t d : : s e t std::set std::set
然后我这个 s e t set set是新学的,不明白为什么 o p e r a t o r operator operator写结构体里要把大小关系反一下?难道是 s e t set set的特点?

Code:

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
struct Edge{
	int to, next;
}edge[maxn << 1];
int num, head[maxn], n, m, cnt, len, q[maxn], print[maxn], d[maxn];
struct node{
	int u, d;
}a[maxn];
bool operator < (node a, node b){ return a.d == b.d ? a.u < b.u : a.d < b.d; }
typedef set<node> :: iterator iter;
set<node> S;

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

void addedge(int x, int y){ edge[++num] = (Edge){y, head[x]}, head[x] = num; }

int main(){
	int Q = read();
	while (Q--){
		n = read(), m = read(), cnt = len = num = 0;
		memset(head, 0, sizeof(head));
		for (int i = 1; i <= n; ++i) d[i] = 0, a[i] = (node){i, 0};
		for (int i = 1; i <= m; ++i){
			int x = read(), y = read();
			addedge(x, y), addedge(y, x);
			++a[x].d, ++a[y].d, ++d[x], ++d[y];
		}
		for (int i = 1; i <= n; ++i) S.insert(a[i]);
		int maxd = -1, pos = 0;
		while (!S.empty()){
			int u = (*S.begin()).u; 
			if (d[u] > maxd) maxd = d[u], pos = len;
			S.erase(S.begin());
			q[++len] = u, print[++cnt] = u;
			for (int i = head[u]; i; i = edge[i].next){
				int v = edge[i].to;
				iter it = S.find((node){v, d[v]});
				if (it == S.end()) continue;
				S.erase(it), q[++len] = v;
				for (int j = head[v]; j; j = edge[j].next){
					int k = edge[j].to;
					it = S.find((node){k, d[k]});
					if (it == S.end()) continue;
					S.erase(it), S.insert((node){k, --d[k]});
				}
			}
		}
		printf("%d ", len - pos);
		for (int i = pos + 1; i <= len; ++i) printf("%d ", q[i]);
		puts("");
		printf("%d ", cnt);
		for (int i = 1; i <= cnt; ++i) printf("%d ", print[i]);
		puts("");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值