CodeForces 201 E.Thoroughly Bureaucratic Organization(贪心+二分+鸽巢原理)

本文探讨了一个关于排列的问题:如何在最坏情况下利用最少的询问次数来还原一个未知的排列序列。通过二分查找与组合数学的方法,设计了一种有效的算法,并提供了完整的实现代码。

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

Description

有一个1~n的排列,每次询问至多可以询问m个位置的数字,但是只会给出乱序后的这些数字,问最坏情况下最少多少次可以还原整个序列

Input

第一行一整数T表示用例组数,每组用例输入两个整数n,m(1T1000,1n,m109)

Output

对于每组用例,输出最坏情况下还原整个序列所需的最少询问次数

Sample Input

5
4 1
4 2
7 3
1 1
42 7

Sample Output

3
2
3
0
11

Solution

如果对于任意k,可以知道k次询问,每次询问至多问m个数字可以还原的最长排列长度为n,那么就可以二分得到答案,把每个数字在这k次询问是否被问到看作一个长度为k01串,第i次询问了这个数字则第i位是1,否则是0,那么我们要找的是尽可能多的这种串,且串之间两两可区分,且对于所有串的某一位,其1的个数不能超过m,因为一次询问至多询问m个数字,首先不考虑这个限制,那么就是至多用km1去构造尽可能多的可两两区分的长度为k01串,那么我们可以贪心的先构造C1k个只含一个1且位置不同的串,然后构造C2k个只含两个1且这两个1位置不完全相同的串,….,以此类推可以得到尽可能多的串,下面证明加入这个限制和不加是一样的,假设i位的1的数量大于,那么由于总数不超过km,所以必然存在一个j位其1的数量小于m,记x为第i位是1且第j位是0的串的个数,y为第i位是0且第j位是1的串的个数,显然x>y,从x中选一个串,交换其在第i位和第j位的值,结果是要么该串变成一个和其他串都可区分的串,要么其和这y个串中某一个相同,由于x>y,由鸽巢原理,必然存在一个串,交换其两位后和这y个串均不相同,那么这个串和其他所有串都可区分,x的数量减一,经过有限次这样的操作,必然可以使得那些使用超过m1的位消失,故该限制没用

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
int T,n,m;
bool check(int k)
{
    if(n==1)return 1;
    ll num=(ll)m*k,res=1,C=1;
    for(int i=1;i<=k;i++)
    {
        if(num<i)return 0;
        if((ll)(k-i+1)*C>=(ll)n*i)C=n;
        else C=C*(k-i+1)/i;
        ll temp=min(num/i,C);
        res+=temp;
        if(res>=n)return 1;
        num-=temp*i;
    }
    return 0;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        int l=0,r=n,mid,ans;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(mid))ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值