1 条题解

  • 0
    @ 2025-8-24 22:55:05

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar IamZZ
    博士,无须担心,答案就藏在下一次尝试中!

    搬运于2025-08-24 22:55:05,当前版本为作者最后更新于2024-03-21 16:02:50,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    G组练习总结#13

    场切题!USACO 真的很喜欢放二分题目(笑)。

    题目大意

    Bessie 又在制造麻烦事!

    她现在有一个长度为 n(n2×105)n(n\le2\times10^5) 的数组 a(ai1011)a(a_i\le10^{11}),她现在想要用自己发明的排序方法让其排好序。

    具体来说,她的排序方法是:

    首先,她会新建一个数组 tt,用来记录排好序后的 aa

    然后 Bessie 会自己选取一些数自己处理,假设她目前手上有 pp 个数,她需要花费 pp 的时间去找到最小的数并马上把它加入 tt 的最后。

    简单来说,设一开始的集合大小是 pp,第一个数会在 pp 时加入,第二个数会在 2p12p-1 时,第三个会在 3p33p-3 以此类推。

    但她还有另一种选择,她可以把数交给朋友们来操作,朋友们很懒,假设他们拿到的数是 vv,他们先睡 vv,在 vv 时加入 tt 末尾。

    加入的时候,如果 Bessie 和朋友同时加数,Bessie 优先级更高。

    现在 Bessie 也想去睡觉啦(不是),她想知道最短的加入所有数的时间。

    数据有 T(T10)T(T\leq10) 组。

    解题思路(暴力)

    处理肯定按照顺序,所以我们可以直接将 aa 从小到大排序。

    我们考虑最后一个数 ana_n 是由谁处理的,如果是 Bessie,所需要的时间就是 p(p+1)2\frac{p(p+1)}2,如果是朋友们,那就是 ana_n

    因此,我们考虑枚举集合大小 pp,判断当前钦定的 pp 是否可以让 ana_n 被选入集合?

    至于向集合内加数需要满足,比如,加第一个时,我们要把 ai<pa_i<p 的数都交给朋友们,然后把第一个 ajpa_j\ge p 的数交给 Bessie。

    为什么要这样?因为 Bessie 加入的第一个数肯定在 pp 时加入,那 ai<pa_i<p 的必须满足在她之前处理完了,所以要找朋友帮忙。

    而且注意,因为 Bessie 的优先级大于朋友们,所以如果她们同时在 pp 加入数,必须满足 Bessie 手上的数小于或等于朋友的,固 aja_j 等于 pp 的情况不能漏。

    对于其他的情况,也都是这样处理,依次处理完所有 pp 个数,判断是否处理完了 ana_n

    时间复杂度 Θ(nA)\Theta(n\sqrt A)AAaa 的值域。

    参考代码(暴力)

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    const int N=2e5+5;
    typedef long long ll;
    int t,n,c;
    ll a[N],m,p;
    int main() {
    	int i,j;
    	scanf("%d",&t);
    	while(t) {
    		--t;
    		scanf("%d",&n);
    		for(i=1; i<=n; ++i)
    			scanf("%lld",a+i);
    		sort(a+1,a+n+1);
    		m=a[n];
    		for(j=1; 1ll*(j+1)*j/2<=a[n]; ++j) {//枚举集合大小p
    			c=j;
    			p=0;
    			for(i=1; i<=n; ++i) {
    				if(!c)
    					break;
    				if(a[i]>=p+c) {//依次往集合里面塞数
    					p=p+c;//记录当前Bessie加数的时间
    					--c;
    				}
    				if(i==n)
    					m=min(m,1ll*j*(j+1)/2);//找答案
    			}
    			if(m<a[n])
    				break;
    		}
    		printf("%lld\n",m);
    	}
    	return 0;
    }
    

    解题思路(正解)

    突然发现几乎没有什么好说的啊……

    这个题目的答案具有单调性啊,集合大小的可行性是有单调性的?

    没错,这个也挺好想的,越大的集合我们就可以处理更多数!

    这么好的代码(不是),一改就可以适应一个二分对吧!

    新的时间复杂度 Θ(nlog2A)\Theta(n\log_2\sqrt A),完结撒花(●'◡'●)!

    参考代码(正解)

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    const int N=2e5+5;
    typedef long long ll;
    int t,n,l,r,d;
    ll a[N],m,p;
    int check(int c) {
    	int i;
    	p=0;
    	for(i=1; i<=n; ++i) {
    		if(!c)
    			return 0;
    		if(a[i]>=p+c) {
    			p=p+c;
    			--c;
    		}
    	}
    	return 1;
    }
    int main() {
    	int i,j;
    	scanf("%d",&t);
    	while(t) {
    		--t;
    		scanf("%d",&n);
    		for(i=1; i<=n; ++i)
    			scanf("%lld",a+i);
    		sort(a+1,a+n+1);
    		m=a[n];
    		l=1,r=sqrt(a[n]);
    		while(l<=r) {//只不过改了,枚举变成二分就好啦~
    			d=l+r>>1;
    			if(check(d)) {
    				r=d-1;
    				m=1ll*d*(d+1)/2;
    			} else
    				l=d+1;
    		}
    		printf("%lld\n",m);
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    9711
    时间
    2000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者