E. OpenStreetMap(单调队列)

  题意:给一个矩阵求a*b的子矩阵里最小值之和

  思路是开n个单调队列,然后当j>=b时,n个单调队列就会维护每一行每个长度为b的区间里的最小值,然后我们要从这n个单调队列的队头里,每a个取一个最小值(此最小值即为对应的a*b矩阵里的最小值),所以可以再用单调队列优化为O(N),不然直接暴力N*A会t。

#include<bits/stdc++.h>
using namespace std;
#define ls rt<<1
#define rs (rt<<1)+1
#define ll long long
#define fuck(x) cout<<#x<<"     "<<x<<endl;
typedef  pair<int,int> pii;
const int maxn=3e3+10;
int d[4][2]={1,0,-1,0,0,1,0,-1};
int q[maxn][maxn],h[maxn],t[maxn];
pii qq[maxn];
ll g[(int)1e7],mod,x,y,mp[maxn][maxn];
int main()
{
    int n,m,a,b;
    ll ans=0;
    scanf("%d%d%d%d%I64d%I64d%I64d%I64d",&n,&m,&a,&b,&(g[0]),&x,&y,&mod);
    for(int i=1;i<=n;i++) h[i]=1,t[i]=0;
    for(int i=1;i<=1e7-5;i++)
        g[i]=(g[i-1]*x+y)%mod;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            mp[i][j]=g[(i-1)*m+j-1];
    for(int j=1;j<=m;j++)
    {
        for(int i=1;i<=n;i++)
        {
            while(h[i]<=t[i]&&mp[i][q[i][t[i]]]>=mp[i][j]) t[i]--;
            q[i][++t[i]]=j;
            while(j-q[i][h[i]]>=b) h[i]++;
        }
        //cout<<endl;
        //for(int i=1;i<=n;i++) cout<<mp[i][q[i][h[i]]<<"    ";
        //cout<<endl;
        if(j>=b)
        {
            int head=1,tail=0;
            for (int i = 1; i <=n;i++)
            {
               while(head<=tail&&mp[qq[tail].first][qq[tail].second]>=mp[i][q[i][h[i]]]) tail--;
               qq[++tail]=make_pair(i,q[i][h[i]]);
               while(i-qq[head].first>=a) head++;
               if(i>=a)
                   ans+=mp[qq[head].first][qq[head].second];
            }
        }

    }
    printf("%I64d\n",ans);
    return 0;
}

 

模板

#include<bits/stdc++.h>//单调队列又称为滑动窗口
using namespace std;
#define ls rt<<1
#define rs (rt<<1)+1
#define ll long long
#define fuck(x) cout<<#x<<"     "<<x<<endl;
const int maxn=1e6+10;
int d[4][2]={1,0,-1,0,0,1,0,-1};
int a[maxn],q[maxn],ans1[maxn],ans2[maxn];



int main()
{
    int n,m,h,t;
    cin>>n>>m;
    for(int i=1;i<=n;i++) scanf("%d",&(a[i]));
    h=1,t=0;
    for(int i=1;i<=n;i++)//求m区间内最小值 以i为末尾的m区间  维护增队列
    {
        while(h<=t&&a[q[t]]>=a[i]) t--;//删去队尾的无用元素
        q[++t]=i;
        while(i-q[h]+1>=m+1) h++;//队头删去在所需区间外的元素
        if(i>=m)
            ans1[i-m+1]=a[q[h]];
    }
    h=1,t=0;
    for(int i=1;i<=n;i++)//求m区间内最大值   以i为末尾的m区间
    {
        while(h<=t&&a[q[t]]<=a[i]) t--;//删去队尾的无用元素
        q[++t]=i;
        while(i-q[h]+1>=m+1) h++;//队头删去在所需区间外的元素
        if(i>=m)
            ans2[i-m+1]=a[q[h]];
    }
    for(int i=1;i+m-1<=n;i++) printf("%d ",ans1[i]);
    cout<<endl;
    for(int i=1;i+m-1<=n;i++) printf("%d ",ans2[i]);
    cout<<endl;
    return 0;
}



 

posted @ 2019-07-23 13:16  eason99  阅读(103)  评论(0编辑  收藏  举报