1 条题解

  • 0
    @ 2025-8-24 21:49:37

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar K8He
    あぁ! 夏を今もう一回

    搬运于2025-08-24 21:49:37,当前版本为作者最后更新于2022-01-20 15:15:36,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    P3503 Blocks 题解

    更好的阅读体验

    原题传送门

    思路

    首先我们可以发现,若 alara_l \sim a_r 的平均值大于等于 kk,则这个区间一定可以转化为都大于等于 kk 的。我们就把这个问题化简成了“求最长的平均值大于等于 kk 的子序列”。

    再去化简,可以发现,如果我们把序列中的每一项都减去 kk,再求前缀和 ss,若 sisj0s_i-s_j\ge 0,则区间 [j,i][j,i] 一定满足条件。

    那么我们考虑如何求这种区间。

    不难发现,i<ji<jsi<sjs_i<s_j,则选 ii 当左端点比 jj 更优,则选 jj 当右端点比 ii 更优。

    那么我们去维护一个单调栈存可能最优的左端点,栈中的元素都满足:在栈中 jjii 之上且 si>sjs_i>s_j。(这里自己好好思考一下)

    根据我们维护的单调栈的性质,我们可以发现:

    • 一个元素越靠栈顶,可以和它在一起的右端点越多,但产生的区间越短。
    • 如果一个右端点与栈里的一个元素产生的区间合法,则该右端点与该元素之上的元素一定也能构成合法区间。

    那么我们再从最右边开始枚举右端点,去找最大区间。如果一个右端点与栈顶的左端点可以构成合法区间那就更新答案,并把栈顶弹出(因为再靠左的右端点就算满足条件也没有这个长了),继续看浮出来的新栈顶是否合法。

    记得判断 si0s_i\ge 0 的情况。

    代码

    #include <bits/stdc++.h>
    #define _for(i,a,b) for(int i=a;i<=b;++i)
    #define for_(i,a,b) for(int i=a;i>=b;--i)
    #define ll long long
    using namespace std;
    const int N=1e5+10,inf=0x3f3f3f3f;
    ll n,q,a[N],b[N],k,ans;
    stack<ll>s1,s2;
    int main(){
    	scanf("%lld%lld",&n,&q);
    	_for(i,1,n)scanf("%lld",&a[i]);
    	while(q--){
    		scanf("%lld",&k);
    		ans=0;
    		_for(i,1,n){
    			b[i]=b[i-1]+a[i]-k;
    			if(b[i]>=0)ans=max(ans,(ll)(i));
    			if(s1.empty()||b[i]<b[s1.top()])s1.push(i);
    		}for_(i,n,1){
    			while(!s1.empty()&&b[i]-b[s1.top()]>=0){
    				ans=max(ans,i-s1.top()),s1.pop();
    			}
    		}
    		printf("%lld\n",ans);
    	}
    	return 0;
    }
    
    • 1

    信息

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