菜需捆的 单调栈 刷题记录

单调栈的一些性质(单增为例)

  • 在栈里的元素一定比后面的都要小(当前走到的)
  • 可以访问到第一个小于我的元素下标(就是在栈里第一个小于我的那个元素)
  • 如果我在栈里是第一个元素,说明我比前面的元素都小(考虑最小的那个元素)

 

BZOJ1113: [Poi2008]海报PLA

  • 单调栈扫一遍就是了,给出的矩形的长没有什么用,只看矩形的宽,出栈的时候ans++,最后统计栈里元素个数,加到ans里
  • 注意的地方:当栈里最大元素跟当前相同时不进栈也不加ans
  • 代码:
    #include <bits/stdc++.h>
    #define nmax 250010
     
    using namespace std;
    typedef long long ll;
    int n,idx=0;
    ll ans=0;
    ll s[nmax]; //单调递增的栈
     
    int main(){
        cin>>n;
        ll a,b;
        scanf("%lld%lld",&a,&b);
        s[0]=b;
        for (int i=1; i<n; i++) {
            scanf("%lld%lld",&a,&b);
            //把b入栈
             while(s[idx]>b){ ans++; idx--; }
            if(s[idx]!=b) s[++idx]=b;
        }
        //统计栈内不同的元素个数
        for (int i=idx; i>0; i--){
            if(s[i]!=s[i-1]) ans++;
        }
        ans++;
        cout<<ans<<endl;
        return 0;
    }    
    ヽ(✿゚▽゚)ノ

 

1012: [JSOI2008]最大数maxnumber

  • 题意:要支持两种操作,1.末尾插入一个数,2.查询当前数列中末尾L个数中的最大的数,并输出这个数的值
  • 只需要维护一个单调下降的栈就可以了,因为后面的数比我还大的话我在所有的查询里都不会再有贡献了。
  • 注意还有一个前缀和和二分查找的小技巧
  • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 200005
     3  
     4 using namespace std;
     5 typedef long long ll;
     6 int op,is=0;
     7 ll mod,x,tmp=0,l=0;
     8 char in[10];
     9 ll s[nmax],num[nmax]={0};
    10  
    11 int main(){
    12     scanf("%ld%lld",&op,&mod);
    13     for (int i=0; i<op; i++) {
    14         scanf("%s%lld",in,&x);
    15         if( in[0]=='A' ){
    16                 l++;
    17          x=(x+tmp)%mod;
    18             int ta=0;
    19             while( s[is]<=x && is>=0 ) {
    20                 s[is]=x;
    21                 ta+=( num[is]-num[is-1] );
    22                 is--;
    23             }
    24             s[++is]=x;
    25             num[is]=ta+num[is-1]+1;
    26  
    27         }else{
    28             int p=lower_bound(num,num+is+1,l-x+1)-num;
    29             tmp=s[p];
    30             printf("%lld\n",s[p]);
    31         }
    32     }
    33     return 0;
    34 }        
    View Code

 

洛谷P4147 玉蟾宫

  • 最大子矩阵问题
  • 一行一行的处理,保证统计不会漏掉极大子矩阵.
  • 在单调栈里放两个值,下标和长度
  • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 1005
     3 
     4 using namespace std;
     5 char in[10];
     6 char M[nmax][nmax];
     7 int n,m,is;
     8 int t[nmax][nmax],d[nmax][nmax];
     9 int s[nmax][2];
    10 
    11 int main(){
    12     cin>>n>>m;
    13     for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) { scanf("%s",in); M[i][j]=in[0]; }
    14     for (int i=1; i<=m; i++) {
    15         int nowr=0;
    16         for (int j=1; j<=n; j++) {
    17             if(M[j][i]=='R') nowr=j;
    18             t[j][i]=j-nowr;  //可行长度
    19         }
    20     }
    21     //开单调栈一顿扫
    22     for (int i=1; i<=n; i++) {
    23         is=0;
    24         s[is][0]=t[i][1];
    25         s[is][1]=1;
    26         for (int j=2; j<=m; j++) {
    27             while(t[i][j]<s[is][0]){  //0是值,1是下标
    28                 int l;
    29                 for (l=is-1; s[l][0]==s[is][0]&&l>=0; l--){}
    30                 l=s[l][1];
    31                 d[i][ s[is][1] ]=s[is][0]*(j-l-1);
    32                 is--;
    33             }
    34             is++;
    35             s[is][1]=j;
    36             s[is][0]=t[i][j];
    37         }
    38         for (; is>=0; is--){
    39             int l;
    40             for (l=is-1; s[l][0]==s[is][0]&&l>=0; l--){}
    41             l=s[l][1];
    42             d[i][ s[is][1] ]=s[is][0]*(m-l);
    43         }
    44     }
    45     int ans=0;
    46     for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) ans=max(ans,d[i][j]);
    47     cout<<3*ans<<endl;
    48     return 0;
    49 }
    哦呼

 

BZOJ1057 [ZJOI2007]棋盘制作

  • 跟上面一题一样,注意横排不合法时推入长度为0的
  • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 2005
     3 
     4 using namespace std;
     5 char in[10];
     6 int M[nmax][nmax];
     7 int n,m,is;
     8 int t[nmax][nmax],d[nmax][nmax],d2[nmax][nmax];
     9 int s[nmax][2];
    10 
    11 void mypop(int i,int x){  //把is弹出去
    12     if(!s[is][0]) { is--; return; }
    13     int l;
    14     for (l=is-1; s[l][0]==s[is][0]&&l>=0; l--){}
    15     if( s[l][0]!=s[is][0] ) l=s[l][1]+1;
    16     if(is==0) l=1;
    17     d[i][ s[is][1] ]=s[is][0]*(x-l+1);
    18     int t=min( x-l+1 , s[is][0] );
    19     d2[i][ s[is][1] ]=t*t;
    20     is--;
    21 }
    22 
    23 int main(){
    24     cin>>n>>m;
    25     for (int i=1; i<=n; i++) for (int j=1; j<=m; j++)  scanf("%d",&M[i][j]);
    26     for (int i=1; i<=m; i++) {
    27         int nowr=0;
    28         for (int j=1; j<=n; j++) {
    29             t[j][i]=j-nowr;  //可行长度
    30             if(M[j][i]==M[j+1][i]) nowr=j;
    31         }
    32     }
    33     for (int i=1; i<=n; i++) {
    34         is=0;
    35         s[is][0]=t[i][1];//0是值,1是下标
    36         s[is][1]=1;
    37         for (int j=2; j<=m; j++) {
    38             if( M[i][j] == M[i][j-1] ) { while(is>=0) mypop(i,j-1); s[++is][1]=j-1; s[is][0]=0; }
    39             else while(t[i][j]<s[is][0]) mypop(i,j-1);
    40             s[++is][1]=j;
    41             s[is][0]=t[i][j];
    42         }
    43         while(is>=0) mypop(i,m);
    44     }
    45     int ans1=0,ans2=0;
    46     for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) { ans1=max(ans1,d[i][j]); ans2=max(ans2,d2[i][j]); }
    47     cout<<ans2<<endl<<ans1<<endl;
    48     return 0;
    49 }
    quq

     

posted @ 2019-08-03 21:13  连昵称都不能重复  阅读(166)  评论(0编辑  收藏  举报