单调队列

22 篇文章 0 订阅
3 篇文章 0 订阅

修剪草坪(mowlawn)

题目描述:
在一年前赢得了小镇的最佳草坪比赛后,FJ变得很懒,再也没有修剪过草坪。现在,新一轮的最佳草坪比赛又开始了,FJ希望能够再次夺冠。然而,FJ的草坪非常脏乱,因此,FJ只能够让他的奶牛来完成这项工作。FJ有N(1 <= N <= 100,000)只排成一排的奶牛,编号为1…N。每只奶牛的效率是不同的,奶牛i的效率为E_i(0 <= E_i<= 1,000,000,000)。靠近的奶牛们很熟悉,因此,如果FJ安排超过K只连续的奶牛,那么,这些奶牛就会罢工去开派对:)。因此,现在FJ需要你的帮助,计算FJ可以得到的最大效率,并且该方案中没有连续的超过K只奶牛。
输入格式:
第一行:空格隔开的两个整数N和K* 第二到N+1行:第i+1行有一个整数E_i
输出格式:
第一行:一个值,表示FJ可以得到的最大的效率值。
输入数据:
5 2
1
2
3
4
5
输入解释:
FJ有5只奶牛,他们的效率为1,2,3,4,5。他们希望选取效率总和最大的奶牛,但是
他不能选取超过2只连续的奶牛
输出数据:
12
输出解释:FJ可以选择出了第三只以外的其他奶牛,总的效率为1+2+4+5=12。

#include<bits/stdc++.h>
using namespace std;
const int N=100003;
long long sum,f[N],ans;
int q[N],h,t,n,m,i,x;
int main(){
    scanf("%d%d",&n,&m);
    ans=1e15;
    for (i=1;i<=n;i++){
        scanf("%d",&x);
        sum+=x;
        while (h<=t && i-q[h]-1>m) h++;
        f[i]=f[q[h]]+x;
        while (h<=t && f[i]<=f[q[t]]) t--;
        q[++t]=i;
    }
    for (i=n-m;i<=n;i++) ans=min(ans,f[i]);
    printf("%lld",sum-ans);
}

旅行(travel)

题目描述:
Matrix67要带他的女友进行一次环城旅行。他选择了一条总长度为L的封闭路线。他所选择的路线上有n个景点。他将从某个景点出发,顺时针开车绕城市一周,再回到出发点。出发前,油箱为空。每个景点内都有一个加油站,第i个景点的加油站提供的油可供车行驶Si的路程。所有加油站可提供的油的总和恰好够汽车行驶一周(即S1+S2+…+Sn=L)。汽车的油箱总能够容下所得到的汽油。临行前的那一天,Matrix67突然意识到,虽然所有加油站的油的总和等于汽车环城一周要消耗的油,但汽车不一定能环城一周,因为有可能还没有到下一个景点,汽油就用光了。显然,是否会发生半路上没油的情况取决于Matrix67选择的出发点。他可不希望和MM的这次浪漫的旅行就这样泡汤了。他知道每个景点离它前一个景点有多远,也知道每个景点的加油站提供的油量。他希望你能帮助他找出,从哪些景点出发可以顺利绕城一周。
输入格式:
第一行为两个正整数,分别代表n和L。输入数据保证n<=500 000,L<=100 000 000,并且n<=L。
第二行到第n+1行这n行数据将按环行道路上顺时针的顺序依次描述各个景点。每行有两个正整数。第i+1行为Di和Si,分别表示第i个景点离其前一个景点(即第i-1个景点)的距离和这个景点的加油站所提供的油可供汽车行驶的距离。其中,D1=0。最后一个(第n个)景点与第一个景点之间的距离没有直接给出,但可以根据输入数据计算出来。
输入数据保证,所有的Si之和等于L,所有的Di之和小于L。
输出格式:
一行正整数。输出时,每两个正整数之间都有一个空格,行末无空格。它们表示所有能够作为出发点顺利完成环城旅行的景点。
当存在多个这样的景点时,请按照升序排列输出他们的序号。
当不存在这样的景点时,输出-1。
输入样例:
5 10
0 1
2 2
3 2
2 2
2 3
输出样例:
3 4 5

#include<bits/stdc++.h>
using namespace std;
const int N=500003;
int n,l,f[N],s[N],d[N],mn,fl,i,sum;
int main(){
    scanf("%d%d",&n,&l);
    for (i=1;i<=n;i++) scanf("%d%d",&d[i-1],&s[i]),sum+=d[i-1];
    d[n]=l-sum;
    sum=0;mn=1e9;
    for (i=1;i<=n;i++){
        f[i]=s[i]-d[i];
        sum+=f[i];
        mn=min(mn,sum);
    }
    for (i=1;i<=n;i++){
        mn-=f[i-1];
        if (mn>=0){
            if (fl) putchar(' ');
            printf("%d",i);
            fl=1;
        }
    }
    if (!fl) printf("-1");
}

产品(product)

问题描述:
在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行。由于这N台机器的性能不同,它们完成每一个步骤的所需时间也不同。机器i完成第j个步骤的时间为T[i,j]。把半成品从一台机器上搬到另一台机器上也需要一定的时间K。同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤。也就是说,如果有一台机器连续完成了产品的L个步骤,下一个步骤就必须换一台机器来完成。现在,dd_engi的OI商店有史以来的第一个产品就要开始生产了,那么最短需要多长时间呢?
某日Azuki.7对跃动说:这样的题目太简单,我们把题目的范围改一改
对于菜鸟跃动来说,这是个很困难的问题,他希望你能帮他解决这个问题
输入格式:
第一行有四个整数M, N, K, L
下面的N行,每行有M个整数。第I+1行的第J个整数为T[J,I]。
输出格式:
输出只有一行,表示需要的最短时间。
输入样例:
3 2 0 2
2 2 3
1 3 1
输出样例:
4
数据范围:
对于50%的数据,N<=5,L<=4,M<=10000
对于100%的数据,N<=5, L<=50000,M<=100000

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define F first
#define S second
const int N=100003;
int n,m,k,l,i,j,t,w[6][N],f[N][6],ans;
list<pair<int,int> >s[6];
int main(){
    scanf("%d%d%d%d",&m,&n,&k,&l);
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++) scanf("%d",&w[i][j]),w[i][j]+=w[i][j-1];
    for (i=1;i<=n;i++) s[i].pb(mp(0,i));
    for (i=1;i<=m;i++){
        for (j=1;j<=n;j++){
            while (!s[j].empty() && i-s[j].front().F>l) s[j].pop_front();
            f[i][j]=f[s[j].front().F][s[j].front().S]+w[j][i]-w[j][s[j].front().F]+k;
        }
        for (j=1;j<=n;j++)
            for (t=1;t<=n;t++)
                if (t!=j){
                    while (!s[t].empty() && f[i][j]-w[t][i]<=f[s[t].back().F][s[t].back().S]-w[t][s[t].back().F]) s[t].pop_back();
                    s[t].pb(mp(i,j));
                }
    }
    ans=1e9;
    for (i=1;i<=n;i++) ans=min(ans,f[m][i]);
    printf("%d",ans-k);
}

patrik(patrik.pas)

题目描述:
有N个人,每个人有一个身高,他们排成一条直线,求有多少对人能够互相看到对方。看到对方的条件为:两个人相邻或两个人之间不存在任何人比他们高。
输入格式:
第一行为N,表示有N(1<=N<=500,000)个人。以下第i+1至N+1行,每行一个整数,表示第i个人的身高(小于2^31)。
输出格式:
符合条件的对数。
输入样例:
7
2
4
1
2
2
5
1
输出样例:
10
样例解释:
2 4
4 1
1 2
2 2
2 5
5 1
4 1 2
2 2 5
4 1 2 2
4 1 2 2 5

数据范围:
对于30%的数据,1<=N<=10000;
对于100%的数据,1<=N<=500,000

#include<bits/stdc++.h>
using namespace std;
const int N=500003;
int n,i,x,top,st[N],p;
long long ans;
int read(){
    int x=0;char c;
    do c=getchar();while (c<'0'||c>'9');
    while ('0'<=c && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}
int find(int x){
    int l=1,r=top+1;
    while (l+1<r){
        int m=l+r>>1;
        if (x<st[m]) l=m;
        else r=m;
    }
    return top-l+1;
}
int main(){
    n=read();
    for (i=1;i<=n;i++){
        x=read();
        ans+=find(x);
        while (top && x>st[top]) top--;
        st[++top]=x;
    }
    printf("%lld",ans);
}

股票交易(trade 2秒)

【题目描述】
最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。
通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi>=BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BSi股。
另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过MaxP。
在第1天之前,lxhgww手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T天以后,lxhgww想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?
【输入】
输入数据第一行包括3个整数,分别是T,MaxP,W。
接下来T行,第i行代表第i-1天的股票走势,每行4个整数,分别表示APi,BPi,ASi,BSi。
【输出】
输出数据为一行,包括1个数字,表示lxhgww能赚到的最多的钱数。
【样例输入】
5 2 0
2 1 1 1
2 1 1 1
3 2 1 1
4 3 1 1
5 4 1 1
【样例输出】
3
【数据范围】
0<=W小于T<=2000,1<=MaxP<=2000

#include<bits/stdc++.h>
using namespace std;
int n,m,w,ap,bp,as,bs,i,j,k,st,ed,f[2003][2003],q[2003],ans;
int main(){
    scanf("%d%d%d",&n,&m,&w);
    memset(f,-63,sizeof(f));
    for (i=1;i<=n;i++){
        scanf("%d%d%d%d",&ap,&bp,&as,&bs);
        for (j=0;j<=as;j++) f[i][j]=-ap*j;
        for (j=0;j<=m;j++) f[i][j]=max(f[i][j],f[i-1][j]);
        k=i-w-1;
        if (k>=0){
            st=ed=0;
            for (j=0;j<=m;j++){
                while (st<ed && q[st]<j-as) st++;
                if (st<ed) f[i][j]=max(f[i][j],f[k][q[st]]+ap*(q[st]-j));
                while (st<ed && f[k][j]+ap*j>=f[k][q[ed-1]]+ap*q[ed-1]) ed--;
                q[ed++]=j;
            }
            st=ed=0;
            for (j=m;j>=0;j--){
                while (st<ed && q[st]>j+bs) st++;
                if (st<ed) f[i][j]=max(f[i][j],f[k][q[st]]+bp*(q[st]-j));
                while (st<ed && f[k][j]+bp*j>=f[k][q[ed-1]]+bp*q[ed-1]) ed--;
                q[ed++]=j;
            }
        }
        ans=max(ans,f[i][0]);
    }
    printf("%d",ans);
}

poj2559 Largest Rectangle in a Histogram

//https://blog.csdn.net/better_space/article/details/52266321
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100003;
int i,n,top,cnt,st[N];
ll rec[N],ans;
inline ll read(){
    char c;ll x=0,f=1;
    do{c=getchar();if(c=='-')f=-1;}while(c<48||c>57);
    do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);
    return f*x;
}
int main(){
    while (~scanf("%d",&n)){
        if (!n) return 0;
        for (i=0;i<n;i++) rec[i]=read();
        rec[n]=-1;ans=-1<<30;
        for (i=0;i<=n;i++){
            if (!top || rec[st[top-1]]<rec[i]) st[top++]=i;
            else{
                if (rec[i]<rec[st[top-1]]){
                    while (top && rec[i]<=rec[st[top-1]]){
                        ans=max(ans,(i-st[top-1])*rec[st[top-1]]);
                        cnt=st[--top];
                    }
                    st[top++]=cnt;
                    rec[cnt]=rec[i];
                }
            }
        }
        printf("%lld\n",ans);
    }
}

BZOJ 1012 最大数maxnumber

#include<bits/stdc++.h>
using namespace std;
const int inf=1<<30;
int m,M,n,k,i,t,tree[800003];
char c[2];
int query(int root,int l,int r,int x,int y){
    if (x>r || y<l) return -inf;
    if (x<=l && r<=y) return tree[root];
    int mid=l+r>>1;
    return max(query(root<<1,l,mid,x,y),query(root<<1|1,mid+1,r,x,y));
}
void update(int root,int l,int r,int pos,int val){
    if (r<pos || l>pos) return;
    if (l==r){
        tree[root]=val;
        return;
    }
    int mid=l+r>>1;
    update(root<<1,l,mid,k,val);
    update(root<<1|1,mid+1,r,k,val);
    tree[root]=max(tree[root<<1],tree[root<<1|1]);
}
int main(){
    cin>>m>>M;
    memset(tree,-63,sizeof(tree));
    for (i=0;i<m;i++){
        scanf("%s%d",c,&n);
        if (c[0]=='A') k++,update(1,1,m,k,(t+n)%M);
          else{
            if (n) printf("%d\n",t=query(1,1,m,k-n+1,k));
            else printf("0\n");
          }
    }
}


BZOJ 1047 理想的正方形

#include<bits/stdc++.h>
using namespace std;
const int inf=(int)1e9;
int n,m,k,i,j,l,r,a[1003],b[1003],f[1003][1003],x,mn,A[1003],B[1003],F[1003][1003],L,R;
int read(){
    int re=0;
    char ch;
    do ch=getchar();while (ch<'0'||ch>'9');
    while (ch>='0' && ch<='9') re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
int main(){
    cin>>n>>m>>k;
    for (i=1;i<=n;i++){
        l=0;r=0;
        L=0;R=0;
        for (j=1;j<=m;j++){
            x=read();
            if (x<a[r] || j==1) a[++r]=x,b[r]=j;
            else{
                while (l<=r && x>=a[r]) r--;
                r++;
                a[r]=x;b[r]=j;
            }
            while (b[l]<=j-k || l==0) l++;
            f[i][j]=a[l];
            if (x>A[R] || j==1) A[++R]=x,B[R]=j;
            else{
                while (L<=R && x<=A[R]) R--;
                R++;
                A[R]=x;B[R]=j;
            }
            while (B[L]<=j-k || L==0) L++;
            F[i][j]=A[L];
        }
    }
    mn=inf;
    for (j=k;j<=m;j++){
        l=0;r=0;
        L=0;R=0;
        for (i=1;i<=n;i++){
            x=f[i][j];
            if (x<a[r] || i==1) a[++r]=x,b[r]=i;
            else{
                while (l<=r && x>=a[r]) r--;
                r++;
                a[r]=x;b[r]=i;
            }
            while (b[l]<=i-k || l==0) l++;
            x=F[i][j];
            if (x>A[R] || i==1) A[++R]=x,B[R]=i;
            else{
                while (L<=R && x<=A[R]) R--;
                R++;
                A[R]=x;B[R]=i;
            }
            while (B[L]<=i-k || L==0) L++;
            if (i>=k) mn=min(mn,a[l]-A[L]);
        }
    }
    cout<<mn;
}

HDU 3530 Subsequence

#include<bits/stdc++.h>
using namespace std;
const int N=100003;
int n,x,y,i,h1,t1,h2,t2,num,las,ans,q1[N],q2[N],a[N];
int main(){
    while (~scanf("%d%d%d",&n,&x,&y)){
        h1=t1=h2=t2=1;q1[1]=q2[1]=ans=las=0;
        for (i=1;i<=n;i++) scanf("%d",&a[i]);
        for (i=1;i<=n;i++){
            while (h1<=t1 && a[i]>a[q1[t1]]) t1--;
            while (h2<=t2 && a[i]<a[q2[t2]]) t2--;
            q1[++t1]=q2[++t2]=i;
            while (h1<=t1 && h2<=t2 && a[q1[h1]]-a[q2[h2]]>y){
                las=min(q1[h1],q2[h2]);
                if (q1[h1]<q2[h2]) h1++;
                else if (q1[h1]>q2[h2]) h2++;
                else h1++,h2++;
            }
            if (h1<=t1 && h2<=t2 && a[q1[h1]]-a[q2[h2]]>=x) ans=max(ans,i-las);
        }
        printf("%d\n",ans);
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值