1 条题解

  • 0
    @ 2025-8-24 22:02:44

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar LlLlCc
    背井离乡,忍辱负重

    搬运于2025-08-24 22:02:43,当前版本为作者最后更新于2020-05-07 11:12:25,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这题网上好像找不到比较详细的题解


    先解决第一问,如何求出最大队伍数

    我们先按aia_{i}从小到大排序一趟,显然,如果ai+1a_{i+1}满足那么aia_{i}也必定满足。手玩几组数据易得,每组人员都是连接在一起的,即:ai,ai+1,ai+2,a_{i},a_{i+1},a_{i+2},\ldots

    我们开两个数组来记录:

    MaxiMax_{i} : 前ii个人最多分几组

    fif_{i} : 第ii个人为一组的结尾最多分几组

    转移方程非常简洁,即:

    fi=Maxiai+1f_{i} = Max_{i-a_{i}}+1

    当然前提必须保证: i>=aii>=a_{i}Maxiai>=0Max_{i-a_{i}}>=0(或者说MaxiaiMax_{i-a_{i}}已经被更新过了)

    再考虑如何更新MaxiMax_{i}

    根据定义,MaxiMax_{i} 表示前ii个人最多分几组,所以 :

    Maxi=max(fi,Maxi1)Max_{i}=max(f_{i},Max_{i-1})

    初值: Max0=0Max_{0}=0

    Ans=fnAns=f_{n} (因为要满足每个小朋友,所以第NN个小朋友必取,AnsAnsfnf_{n}而并非MaxnMax_{n}

    第一问做出来后第二问就容易了

    看到“最小化人数最多的队伍的人数”,自然而然就会想到用二分来枚举答案

    midmid:人数最多的队伍人数

    再开一个数组:

    LstiLst_{i}: 枚举完第 ii 个人时上一组的结尾位置(思考

    因为midmid表示人数最多的队伍人数,所以: iLstiai<=midi-Lst_{i-a_{i}}<=mid

    同时更新操作也和MaxMax数组类似:

    fi>=Maxi1f_{i}>=Max_{i-1}时:Lsti=iLst_{i}=i (新开一组,自己为结尾位置)

    否则: Lsti=Lsti1Lst_{i}=Lst_{i-1} (因为没有新开一组,上一组结尾位置即不变)

    在满足组数最多的情况下,用二分来查找 midmid 的最小值,即为答案

    详见代码:

    #include<bits/stdc++.h>
    #define maxn 1000005
    using namespace std;
    int n,Ans,Min,Max[maxn],f[maxn],lst[maxn],L,R,mid;
    struct lc{
    	int x,id;
    	bool operator <(const lc b)const{return x<b.x;}
    }a[maxn];
    inline int read(){
    	int ret=0;char ch=getchar();
    	while (ch<'0'||ch>'9') ch=getchar();
    	while (ch<='9'&&ch>='0') ret=ret*10+ch-'0',ch=getchar();
    	return ret;
    }
    inline int check(int len){
    	memset(lst,0,sizeof lst);
    	memset(f,-1,sizeof f);
    	memset(Max,-1,sizeof Max);
    	Max[0]=0;int inf=Max[1];
    	for (int i=1;i<=n;i++){
    		if (a[i].x<=i&&Max[i-a[i].x]>=0&&lst[i-a[i].x]+len>=i) f[i]=Max[i-a[i].x]+1;     //判断是否能新成一个组 
    		if (f[i]>=Max[i-1]) lst[i]=i,Max[i]=f[i];
    		else lst[i]=lst[i-1],Max[i]=Max[i-1];
    	}
    	return f[n];           //返回最大组数 
    }
    int main(){
    	n=read();
    	for (int i=1;i<=n;i++) a[i]=(lc){read(),i};
    	sort(a+1,a+n+1); 
    	printf("%d\n",Min=check(n));     //记录最大组数 
    	L=0,Ans=R=n;
    	while (L<=R){
    		mid=L+R>>1;
    		if (check(mid)==Min) Ans=mid,R=mid-1;       //查找满足最大组数的前提下mid最小为多少 
    		else L=mid+1;
    	}
    	check(Ans),R=n;       //最后要在check一遍来计算lst数组 
    	while (R){  
    		L=lst[R-a[R].x];
    		printf("%d",R-L);
    		for (int i=L+1;i<=R;i++) printf(" %d",a[i].id); //输出答案 
    	    putchar('\n');R=L;
    	}
    	return 0;
    }
    

    可能讲的不够详细,如有不懂的地方欢迎评论区留言或者私信

    • 1

    信息

    ID
    3689
    时间
    2500ms
    内存
    250MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者