cf547B 单调栈

题目链接

https://codeforces.com/problemset/problem/547/B

题意

长为n数组,一个长为组合是说长为x的连续串,一个组合的权值是他最小元素的值。现在问你对于1-x的每一个长度i,i组合的最大权是多少。

思路

单调栈

我们考虑一个数字对答案的影响。如果要这个数字对组合贡献答案,那么他一定要是组合里最小的数字,因此我们对于每一个数字,维护它左右两边第一个小于它的数的下标l和r,也就是维护一下他为最小值的连续区间。假如这个区间长为len,这个数是a,那么我们就可以对1-len的所有答案对a取max。

那么我们怎么维护呢,既然涉及单调性,又是线性,考虑单调栈/队列。我们维护一个不递减的单调栈,扫一遍整个数组,如果比栈顶大,就入栈,否则一直取出栈顶直到可以入栈。注意这里所有取出的前栈顶,他们的r已经找到了(第一个比他小的数)。当我们把数入栈时,当前栈顶就是左边第一个比他小的数,更新l即可。也就是l在入栈时更新,r在出栈时更新。因为存在取空入栈/没有出栈的情况,所以记得处理下初始化。

我们维护了所有数字对应区间,下面就是刚说的,对1-len的所有答案取max。这里开始的时候想麻烦了,其实只需要对len取下max就行了,之后因为我们每个操作都是1-len取,那么我们直接从n到1倒着取一遍max就可以求出答案了。

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=200505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int ans[maxn],a[maxn],l[maxn],r[maxn];
    struct Node{
        int id,num,l,r;
    }node[maxn];
    stack<int>s;
    bool cmp(Node a,Node b){
        return a.num<b.num;
    }
    
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>node[i].num;
            node[i].id=i;
            node[i].l=0;node[i].r=n+1;
        }
        for(int i=1;i<=n;i++){
            while(s.size()&&node[s.top()].num>node[i].num){
                auto t=s.top();s.pop();
                node[t].r=i;
            }
            if(!s.size()){
                s.push(i);
            }
            else{
                node[i].l=node[s.top()].id;
                s.push(i);
            }
        }
        for(int i=1;i<=n;i++){
            int x=node[i].r-node[i].l-1;
            ans[x]=max(ans[x],node[i].num);
        }
        for(int i=n;i;i--)
            ans[i]=max(ans[i],ans[i+1]);
        for(int i=1;i<=n;i++)
            cout<<ans[i]<<' ';cout<<endl;
        //for(int i=1;i<=n;i++)
        //    cout<<node[i].num<<' ';cout<<endl;
        //for(int i=1;i<=n;i++)
        //    cout<<"i:"<<i<<' '<<node[i].l<<' '<<node[i].r<<endl;
	} 
						
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值