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+1n⌋≤q 且 ⌊ n q + 1 ⌋ ≤ p \lfloor \frac{n}{q+1} \rfloor\le p ⌊q+1n⌋≤p。
请帮助小Q找出一个可行的邀请方案。
solution
有一个比较乱搞的方法,可以看这篇博客。
这道题真的是神仙构造题啊。
我们先考虑把约束 ⌊ n p + 1 ⌋ ≤ q \lfloor \frac{n}{p+1} \rfloor\le q ⌊p+1n⌋≤q 化简。由于 ⌊ n x ⌋ = ⌈ n − x + 1 x ⌉ \lfloor \frac{n}{x} \rfloor=\lceil \frac{n-x+1}{x} \rceil ⌊xn⌋=⌈xn−x+1⌉,我们有:
⌈ n − ( p + 1 ) + 1 p + 1 ⌉ ≤ q \lceil \frac{n-(p+1)+1}{p+1} \rceil\le q ⌈p+1n−(p+1)+1⌉≤q
那把上取整去掉,原式显然也成立,即:
n − p p + 1 ≤ q \frac{n-p}{p+1}\le q p+1n−p≤q
然后乘过去得到 n ≤ p + q + p q n\le p+q+pq n≤p+q+pq,即 n < p + q + p q + 1 = ( p + 1 ) ( q + 1 ) n<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+1n⌋≤p 也能推出一样的不等式。
也就是说,我们只要满足在 p , q p,q p,q 各自满足条件时,再满足 ( p + 1 ) ( q + 1 ) > n (p+1)(q+1)>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=1∑q(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 ) > n (\max\limits_{i=1}^q deg_i+1)q≥∑_{i=1}^q(deg_i+1)=n\Rightarrow(p+1)(q+1)>n (i=1maxqdegi+1)q≥i=1∑q(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;
}