2020牛客暑期多校训练营(第二场) F、Fake Maxpooling (单调队列)

本文介绍了一道编程题目,涉及求解一个nm矩阵中大小为kk子矩阵最大值之和的问题。利用lcm计算和单调队列优化,实现了n*m log n的时间复杂度解法,避免了超时。同时提到了两种其他解决方案,包括不同数组处理和预处理gcd查询的方法。
摘要由CSDN通过智能技术生成

题目链接

题面:
在这里插入图片描述

题意:
给定一个nm的矩阵,其中aij = lcm( i , j )。
求所有大小为k
k的子矩阵的最大值的和。

官方题解:
在这里插入图片描述

不过我的lcm是,n*m log n 求的。。。
还好没T。

代码:
①、
2304ms

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=5100;
const int maxm=100100;
const int up=100000;

int a[maxn][maxn],b[maxn][maxn];
int c[maxn],q[maxn];

int gcd(int a,int b)
{
    if(a==0||b==0) return a|b;
    return gcd(b,a%b);
}

int lcm(int a,int b)
{
    return a/gcd(a,b)*b;
}

int main(void)
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            a[i][j]=lcm(i,j);
    }

    for(int i=1;i<=m;i++)
    {
        int l=1,r=0;
        for(int j=1;j<=n;j++)
        {
            while(l<=r&&q[l]<=j-k) l++;
            while(l<=r&&a[q[r]][i]<=a[j][i]) r--;
            q[++r]=j;
            b[j][i]=a[q[l]][i];
        }
    }

    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int l=1,r=0;
        for(int j=1;j<=m;j++)
        {
            while(l<=r&&q[l]<=j-k) l++;
            while(l<=r&&b[i][q[r]]<=b[i][j]) r--;
            q[++r]=j;
            if(i>=k&&j>=k)ans=ans+b[i][q[l]];
        }
    }
    printf("%lld\n",ans);
    return 0;
}


②、
开三个数组就会MLT。
888ms

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=5100;
const int maxm=100100;
const int up=100000;

int a[maxn][maxn],b[maxn][maxn];
int c[maxn],q[maxn];

int main(void)
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(!b[i][j])//i,j互质
            {
                for(int k=1;k*i<=n&&k*j<=m;k++)
                    b[k*i][k*j]=k,a[k*i][k*j]=i*j*k;
            }
        }
    }

    for(int i=1;i<=m;i++)
    {
        int l=1,r=0;
        for(int j=1;j<=n;j++)
        {
            while(l<=r&&q[l]<=j-k) l++;
            while(l<=r&&a[q[r]][i]<=a[j][i]) r--;
            q[++r]=j;
            b[j][i]=a[q[l]][i];
        }
    }

    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int l=1,r=0;
        for(int j=1;j<=m;j++)
        {
            while(l<=r&&q[l]<=j-k) l++;
            while(l<=r&&b[i][q[r]]<=b[i][j]) r--;
            q[++r]=j;
            if(i>=k&&j>=k)ans=ans+b[i][q[l]];
        }
    }
    printf("%lld\n",ans);
    return 0;
}


③、O(值域预处理),O(1)查询的gcd
1292ms

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=5100;
const int maxm=100100;
const int up=100000;

int prime[maxn],fac[maxn][3],gcd[110][110],cnt=0;
bool ha[maxn];
int a[maxn][maxn],b[maxn][maxn];
int c[maxn],q[maxn];
int ppt;

void Prime(int n)
{
    ppt=sqrt(n)+1;
    fac[1][0]=fac[1][1]=fac[1][2]=1;
    ha[1]=true;
    for(int i=2;i<=n;i++)
    {
        if(!ha[i])
        {
            prime[cnt++]=i;
            fac[i][0]=fac[i][1]=1;
            fac[i][2]=i;
        }
        for(int j=0;j<cnt&&i*prime[j]<=n;j++)
        {
            int now=i*prime[j];
            ha[now]=true;
            fac[now][0]=fac[i][0]*prime[j];
            fac[now][1]=fac[i][1];
            fac[now][2]=fac[i][2];
            if(fac[now][0]>fac[now][1]) swap(fac[now][0],fac[now][1]);
            if(fac[now][1]>fac[now][2]) swap(fac[now][1],fac[now][2]);
            if(i%prime[j]==0) break;
        }
    }
    for(int i=0;i<=ppt;i++) gcd[i][0]=gcd[0][i]=i;
    for(int i=1;i<=ppt;i++)
        for(int j=1;j<=i;j++)
            gcd[i][j]=gcd[j][i]=gcd[j][i%j];
}

int gg(int a,int b)
{
    int ans=1;
    for(int i=0;i<=2;i++)
    {
        int now=0;
        if(fac[a][i]>ppt)
        {
            if(b%fac[a][i]==0) now=fac[a][i];
            else now=1;
        }
        else now=gcd[fac[a][i]][b%fac[a][i]];
        b/=now;
        ans*=now;
    }
    return ans;
}

int main(void)
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    Prime(max(n,m));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            a[i][j]=i*j/gg(i,j);
        }
    }
    for(int i=1;i<=m;i++)
    {
        int l=1,r=0;
        for(int j=1;j<=n;j++)
        {
            while(l<=r&&q[l]<=j-k) l++;
            while(l<=r&&a[q[r]][i]<=a[j][i]) r--;
            q[++r]=j;
            b[j][i]=a[q[l]][i];
        }
    }

    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int l=1,r=0;
        for(int j=1;j<=m;j++)
        {
            while(l<=r&&q[l]<=j-k) l++;
            while(l<=r&&b[i][q[r]]<=b[i][j]) r--;
            q[++r]=j;
            if(i>=k&&j>=k)ans=ans+b[i][q[l]];
        }
    }
    printf("%lld\n",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值