problem
他的联系薄上有 n n n 位好友,他们两两之间或者互相认识,或者互相不认识。
小 Q 希望在周六办一个热闹的聚会,再在周日办一个尴尬的聚会。
- 一场热闹度为 p p p 的聚会请来了任意多位好友,对于每一位到场的好友来说都有至少 p p p 位他认识的好友也参加了聚会,且至少对于一位到场的好友来说现场恰好有 p p p 位他认识的好友;
- 一场尴尬度为 q q q 的聚会请来了恰好 q q q 位好友,且他们两两互不认识。
两场聚会可能有重复的参与者,联系薄上也有可能有某些好友同时缺席了两场聚会。
小 Q 喜欢周六聚会的热闹度 p p p 与周日聚会的尴尬度 q q q 之间满足: ⌊ n p + 1 ⌋ ≤ q \left\lfloor \frac{n}{p+1} \right\rfloor\le q ⌊p+1n⌋≤q 且 ⌊ n q + 1 ⌋ ≤ p \left\lfloor \frac{n}{q+1} \right\rfloor \le p ⌊q+1n⌋≤p。
请帮助小 Q 找出一个可行的邀请方案。
solution
observation1 : \text{observation1}: observation1: ⌊ n p + 1 ⌋ ≤ q ∧ ⌊ n q + 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 , q p,q p,q 即可。
看似是两个独立的部分,实际上他们各自的构造方式是一样的原理。
- 热闹的聚会。即度数限制的图。
每次找出当前图中度数最小的点,更新 p p p 的最大值。
并删掉这个点,动态地改变与之相连的其余点的度数。
将弹出的编号桉顺序记录下来,并记下最大值的位置。
位置以前的点则是不被选的。
-
尴尬的聚会。即独立集。
每次找出当前图中度数最小的且未被标记的点,加入尴尬的聚会。
然后标记与之直接相连的所有点都不能参加聚会。
下面给出该构造的正确性证明:
将每个点加入尴尬的聚会,除去这个点本身,最多会从图中删掉 p p p 个点。显然 q ≥ ⌈ n p + 1 ⌉ q\ge \lceil\frac{n}{p+1}\rceil q≥⌈p+1n⌉。
如果独立集的选点运行了 q q q 次,第 i i i 次删掉的点度数为 d i d_i di。
则有 ∑ i = 1 q ( d i + 1 ) ≥ n \sum_{i=1}^q(d_i+1)\ge n ∑i=1q(di+1)≥n。而 ( q + 1 ) ⋅ max ( d i + 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;
}