2020牛客多校第二场F题Fake Maxpooling

Fake Maxpooling
题意:
一个n×m的矩阵,矩阵元素的值为行数和列数的最小公倍数(least common multiple,LCM),求所有k×k的子矩阵里最大值的和。
思路: (正解为单调队列)
找到子矩阵里行列数互质且乘积最大的那个数。k×k的循环找到这个数肯定会超时的。从正确代码里看到一个机智做法:从子矩阵里最大的那个点也就是右下角开始dfs向上向左搜索,遇到gcd(a,b)==1就return a*b,然后取最大的再返回,这样比一行一列的k×k循环要快很多,因为最大的元素肯定更靠近右下角。

但是测试5000×5000数据会超时,可代码能AC,怀疑数据过水。
dfs并没有对k进行限制,可能搜到的结果不在子矩阵范围内,dp做法也对k没有限制,那么k = 2与k = 3,4,5,1000搜出来的结果是一样的,那么题目改成以(i,j)为子矩阵右下角的最大值的和是否更合理?
代码:

#include 
#include 
#include 
#include 

using namespace std;
typedef long long ll;

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
int dfs(int a, int b)
{
    int x, y;
    if (gcd(a, b) == 1) return a * b;
    else {
        x = dfs(a - 1, b);
        y = dfs(a, b - 1);
    }
    return max(x, y);
}
int main()
{
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    ll ans = 0;
    for(int i = k; i <= n; ++ i)
        for(int j = k; j <= m; ++ j)
        {
            int x = gcd(i, j);
            if(k == 1) ans += i / x * j;
            else if(x == 1) ans += i * j;
            else
            {
                ans += dfs(i, j);
            }
        }
    printf("%lld", ans);
    return 0;
}

dp做法:
dp[i][j]:以(i,j)为左下角矩阵元素的最大值
dp[i][j] = max(lcm(i, j),dp[i - 1][j],dp[i][j - 1])
注意k == 1时的特判,然后根据k将最大值加起来,记得开long long
代码:

#include 
#include 
#include 
#include 

using namespace std;
typedef long long ll;

const int N = 5005;

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
int dp[N][N];

int main()
{
    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)
        {
            dp[i][j] = i / gcd(i, j) * j;
            if(k != 1) dp[i][j] = max(dp[i - 1][j], max(dp[i][j - 1], dp[i][j]));
        }
    }
    ll ans = 0;
    for(int i = k; i <= n; ++ i)
        for(int j = k; j <= m; ++ j)
            ans += dp[i][j]*1ll;
    printf("%lld", ans);
    return 0;
}

你可能感兴趣的:(多校)