problem
luogu-P5361
他的联系薄上有 nnn 位好友,他们两两之间或者互相认识,或者互相不认识。
小 Q 希望在周六办一个热闹的聚会,再在周日办一个尴尬的聚会。
- 一场热闹度为 ppp 的聚会请来了任意多位好友,对于每一位到场的好友来说都有至少 ppp 位他认识的好友也参加了聚会,且至少对于一位到场的好友来说现场恰好有 ppp 位他认识的好友;
- 一场尴尬度为 qqq 的聚会请来了恰好 qqq 位好友,且他们两两互不认识。
两场聚会可能有重复的参与者,联系薄上也有可能有某些好友同时缺席了两场聚会。
小 Q 喜欢周六聚会的热闹度 ppp 与周日聚会的尴尬度 qqq 之间满足:⌊np+1⌋≤q\left\lfloor \frac{n}{p+1} \right\rfloor\le q⌊p+1n⌋≤q 且 ⌊nq+1⌋≤p\left\lfloor \frac{n}{q+1} \right\rfloor \le p⌊q+1n⌋≤p。
请帮助小 Q 找出一个可行的邀请方案。
solution
observation1:\text{observation1}:observation1: ⌊np+1⌋≤q∧⌊nq+1⌋≤p⇔(p+1)(q+1)≥n+1\lfloor\frac{n}{p+1}\rfloor\le q\wedge \lfloor\frac{n}{q+1}\rfloor\le p\Leftrightarrow (p+1)(q+1)\ge n+1⌊p+1n⌋≤q∧⌊q+1n⌋≤p⇔(p+1)(q+1)≥n+1。
我们只需要分别最大化 p,qp,qp,q 即可。
看似是两个独立的部分,实际上他们各自的构造方式是一样的原理。
- 热闹的聚会。即度数限制的图。
每次找出当前图中度数最小的点,更新 ppp 的最大值。
并删掉这个点,动态地改变与之相连的其余点的度数。
将弹出的编号桉顺序记录下来,并记下最大值的位置。
位置以前的点则是不被选的。
-
尴尬的聚会。即独立集。
每次找出当前图中度数最小的且未被标记的点,加入尴尬的聚会。
然后标记与之直接相连的所有点都不能参加聚会。
下面给出该构造的正确性证明:
将每个点加入尴尬的聚会,除去这个点本身,最多会从图中删掉 ppp 个点。显然 q≥⌈np+1⌉q\ge \lceil\frac{n}{p+1}\rceilq≥⌈p+1n⌉。
如果独立集的选点运行了 qqq 次,第 iii 次删掉的点度数为 did_idi。
则有 ∑i=1q(di+1)≥n\sum_{i=1}^q(d_i+1)\ge n∑i=1q(di+1)≥n。而 (q+1)⋅max(di+1)≥n(q+1)·\max(d_i+1)\ge n(q+1)⋅max(di+1)≥n 显然。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 10005
#define Pair pair < int, int >
int T, n, m;
int p[maxn], q[maxn], vis[maxn], deg[maxn], d[maxn];
vector < int > G[maxn];
priority_queue < Pair, vector < Pair >, greater < Pair > > Q;int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ ) deg[i] = 0;for( int i = 1;i <= n;i ++ ) G[i].clear();for( int i = 1, u, v;i <= m;i ++ ) {scanf( "%d %d", &u, &v );G[u].push_back( v );G[v].push_back( u );deg[u] ++, deg[v] ++;}int ans1 = 0, cnt1 = 0, pos;for( int i = 1;i <= n;i ++ ) vis[i] = 0;for( int i = 1;i <= n;i ++ ) d[i] = deg[i];for( int i = 1;i <= n;i ++ ) Q.push( make_pair( d[i], i ) );while( ! Q.empty() ) {int u = Q.top().second, w = Q.top().first; Q.pop();if( vis[u] ) continue; vis[u] = 1;p[++ cnt1] = u;if( w > ans1 ) ans1 = w, pos = cnt1;for( int v : G[u] ) if( ! vis[v] ) Q.push( make_pair( --d[v], v ) );}int ans2 = 0, cnt2 = 0;for( int i = 1;i <= n;i ++ ) vis[i] = 0;for( int i = 1;i <= n;i ++ ) d[i] = deg[i];for( int i = 1;i <= n;i ++ ) Q.push( make_pair( d[i], i ) );while( ! Q.empty() ) {int u = Q.top().second, w = Q.top().first; Q.pop();if( vis[u] ) continue;vis[u] = 1;q[++ cnt2] = u;for( int v : G[u] ) vis[v] = 1;}for( int i = 1;i <= n;i ++ ) vis[i] = 0;for( int i = 1;i <= pos;i ++ ) vis[p[i]] = 1;printf( "%d ", n - pos );for( int i = 1;i <= n;i ++ ) if( ! vis[i] ) printf( "%d ", i );puts("");printf( "%d ", cnt2 );for( int i = 1;i <= cnt2;i ++ ) printf( "%d ", q[i] );puts("");}return 0;
}