Skip to main content
Original206 words1 min

题解:CF234G Practice

Summary

把 n 名选手反复对半分组进行比赛,要求任意两名选手都在某场里被分到不同组,求最少场次及每场分组方案。每场至多让两半选手相互独立,故至少需要以 2 为底 n 的对数向上取整场。构造上按编号的二进制位分组:第 j 场把二进制第 j 位为 0 的选手划入一组,任意两个不同编号至少有一位不同,即可保证被区分。

解题思路​

设 kk 人需要 pkp_k 次比赛,下一场比赛将分成 ii 人和 k−ik-i 人。

显然这 ii 人和 k−ik-i 人相互独立,不需要额外的比赛,所以 pk=max⁡(pi,pk−i)+1p_k=\max(p_i,p_{k-i})+1。显然当 i=k−i=k2i=k-i=\frac{k}{2},即两队人数均匀时,获得最优解。

每次比赛都最多让两半人相互独立,因此至少需要 k=⌈log⁡2n⌉=⌊log⁡2(n−1)⌋+1k=\left\lceil\log_2 n\right\rceil=\left\lfloor\log_2 (n-1)\right\rfloor+1 场比赛。

注意到,任意两个不同的非负整数,至少有一位二进制不同,所以不妨根据第 jj 位二进制进行分组。(0≤j<k0\le j<k)

参考代码​

460 Bcpp
#include <bits/stdc++.h>
using namespace std;

const int N=1005;
int a[N];
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
int k=__lg(n-1)+1;
cout<<k<<'\n';
for(int j=0;j<k;j++)
{
int ans=0;
for(int i=0;i<n;i++)
{
if(!(i>>j&1))ans++;
}
cout<<ans<<' ';
for(int i=0;i<n;i++)
{
if(!(i>>j&1))cout<<i+1<<' ';
}
cout<<'\n';
}
return 0;
}