1 条题解

  • 0
    @ 2025-8-24 22:12:11

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Su_Zipei
    与卿再世相逢日,玉树临风一少年

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

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

    以下是正文


    这里有一个O(n)O(n)的做法

    首先先来考虑下面一种情况,假设第kk次切割时,天数为dkd_k,高度为bkb_k,第k+1k+1次切割时,天数为dk+1d_{k+1},高度为bk+1b_{k+1},那么我们定义一个切割速度,令v=bk+1bkdk+1dkv=\frac{b_{k+1}-b_k}{d_{k+1}-d_k},这个切割速度有什么含义呢,如果在dkd_k天时所有的草都是bkb_k高,那么生长速度>v>v的草都要割掉,注意这里的vv极有可能是一个浮点数,而在写代码的时候最好还是避免浮点数的出现,所以要怎么转化?这个应该很显然,令v=bk+1bkdk+1dkv=\lfloor \frac{b_{k+1}-b_k}{d_{k+1}-d_k} \rfloor即可,因为判断的时候都是找的大于这个vv的,而给出的生长速度都是整数,所以把这个vv下取整后再判断大小也没有任何影响。怎么求出答案呢?很显然,就是(dk+1dk)(d_{k+1}-d_k)*需要割掉的草的总生长速度+t+ttt是什么?分类讨论一下,如果bk>bk+1b_k>b_{k+1},那么除了长出来的草,还需要割掉bkb_kbk+1b_{k+1}高的那一段,也就是(bkbk+1)(b_k-b_{k+1})*需要割掉的草的总数量,如果bk==bk+1b_k==b_{k+1}t=0t=0,如果如果bk<bk+1b_k<b_{k+1},就不能长出来的全都割掉,因为只割到bk+1b_{k+1},所以需要把之前多算的那些减去,即(bk+1bk)-(b_{k+1}-b_k)*需要割掉的草的总数量,综上所述,t=(bkbk+1)t=(b_k-b_{k+1})*需要割掉的草的总数量,于是需要求的就是需要割掉的草的总生长速度和需要割掉的草的总数量,注意到mm最大也就10610^6,所以开10610^6个桶,第ii个桶里边存生长速度为ii的草的数量,然后就可以运用一下前缀和的思想求出上边的两个值。

    好了基本思想有了,但是注意一个问题,并不是所有的需要切割的草在上一次切割时都恰好为bb,即上一次没有被切割但是下一次它长到了可以被切割的高度的草,把这些草。所以上边讨论了五百多字就白扯了吗,显然不是,还是有一部分草是满足上述的切割办法的,也就是两次都被切割的,所以只需要特殊考虑上一次没有被切割但是下一次它长到了可以被切割的高度的草,把这些草处理了就行。通过上一次的切割处理这个很不好处理,但是我们可以发现,如果我们定义第0天时割掉了所有高度大于0的草(对答案没有影响因为初始时都是0),那么假设当前时刻为dkd_k,这些草在之前的某个时刻dt(dt[0,dk)  )d_t(d_t \in[0,d_k)\ \ )一定会被割掉,于是dtd_tdkd_k套用上述式子就行,聪明的你一定会发现这样会重复割草,没错,的确会,为了避免这种情况,假设上次割掉了生长速度超过lastlast的草,那么这次只需要割掉生长速度超过切割速度且小于等于lastlast的草即可,但是这样还是会出现重复割草的情况,因为可能前边已经割过的又被割了一次,所以需要记录前边每一次割草割过的最小切割速度,举个例子吧,假设记录的这个为toito_i,表示的意思就是在第ii次割草的时候生长速度大于toito_i的已经都被割掉了,所以如果此时kik-i的切割速度大于toito_i就需要直接breakbreak掉,避免了重复割草。 但是这样做的时间复杂度应该是O(m2)O(m^2)的,需要优化一下。

    注意到草的生长速度是有单调性的,在不去割草的情况下生长速度越大的高度一定越大废话,于是可以维护一个toto值单调上升的单调栈。若栈顶那一次切割能切到的最小速度\leq当前能切到的最小速度,则前面切不到的依旧切不到,若栈顶那一次切割能切到的最小速度>>当前能切到的最小速度,则先计算比栈顶那一次切割能切到的最小速度大的,并将栈顶poppop掉,重复此过程至栈顶速度\leq当前能切到的最小速度。

    但是这样会不会漏掉什么情况呢?答案是不会,因为每一次的割草都是割的连续的速度,只可能出现重复情况,而重复情况上边已经排除,所以这种做法是可以的。

    由于本人蒟蒻,可能有些地方写的不严谨或者不清楚,又或者有的地方写错了。。。反正感觉不对的地方欢迎指出来。

    借鉴于https://www.cnblogs.com/wangyurzee7/p/5224273.html

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int lqs=5e5+10,m=1e6;
    int day,a[lqs<<1],n,stk[lqs],top;
    ll s[lqs<<1],d[lqs],b[lqs],to[lqs];
    int main(){
    	scanf("%d%d",&n,&day);
    	for(int w,i=1;i<=n;i++)
    		scanf("%d",&w),a[w]++;
    	for(int i=1;i<=day;i++)
    		scanf("%lld%lld",&d[i],&b[i]);
    	for(int i=1;i<=m;i++)
    		s[i]=s[i-1]+1ll*a[i]*i;
    	for(int i=1;i<=m;i++)
    		a[i]+=a[i-1];
    	top=1;
    	for(int i=1;i<=day;i++){
    		ll res=0;
    		int last=m,x=max(to[stk[top]],min((b[i]-b[stk[top]])/(d[i]-d[stk[top]]),1ll*m));
    		while(x<last){
    			res+=(d[i]-d[stk[top]])*(s[last]-s[x])+(b[stk[top]]-b[i])*(a[last]-a[x]);
    			last=x;
    			if(last>to[stk[top]])break;
    			if(top==0)break;top--;
    			x=max(to[stk[top]],min((b[i]-b[stk[top]])/(d[i]-d[stk[top]]),1ll*m));
    		}
    		to[i]=last;
    		if(to[i]<m)stk[++top]=i;
    		printf("%lld\n",res);
    	}
    }
    
    • 1

    信息

    ID
    5063
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者