多校赛8中题

该博客介绍了一道编程题目,涉及矩阵运算和子矩阵元素之和的计算。题目要求根据两个序列构造矩阵,并找出所有和为给定常数的子矩阵数量。博主分享了解题思路,包括使用前缀和优化二分查找的方法,以及代码实现。最终给出了AC(Accepted)的代码示例。
摘要由CSDN通过智能技术生成

(T3)矩阵

题目背景:

14 已经想不出新颖的题目背景了,所以他准备用序列来构造矩阵。

题目描述:

WY (机房巨佬) 给了 14 一个长度为 n n n 的序列 a a a 和一个长度为 m m m 的序列 b b b

14 玩序列玩腻了,准备用这两个序列构造出一个 n × m n\times m n×m 的矩阵 c。

14 规定 c i , j = a i × b i c_{i,j}=a_i \times b_i ci,j=ai×bi,14 是一个好奇的小朋友,

他想知道在这个矩阵中有多少个子矩阵的元素之和为 k k k

其中 k k k 为 14 给定的一个常数。

输入格式:

输入第一行包含三个整数 n , m , k n,m,k n,m,k,表示序列 a a a 的长度,序列 b b b 的长度以及 14 给出的常数。

输入第二行包含 n n n 个整数,表示序列 a a a

输入第三行包含 m m m 个整数,表示序列 b b b

输出格式:

输出只有一行,表示符合条件的子矩阵个数。

样例输入:
3 4 20
2 3 5
4 6 3 2
样例输出:
4

题解:

对于一个左上角为 a i , b i a_i,b_i ai,bi,右下角为 a j , b j a_j,b_j aj,bj,的矩形里面的元素和

是等于 ( ∑ k = i j a k ) × ( ∑ t = i j b t ) (\sum\limits_{k=i}^{j}a_k) \times (\sum\limits_{t=i}^{j}b_t) (k=ijak)×(t=ijbt)

因为是 i i i j j j 范围中间所有的 a a a 数组里值的和

就可以先求一个前缀和

b b b 数组同理

保证可以二分的条件:

因为所有的 a i , b i a_i,b_i ai,bi 值都是 1 e 9 ≥ a i , b i ≥ 0 1e9\ge a_i,b_i \ge0 1e9ai,bi0

所以在确定左端点的情况下它是单调递增的。

k k k 的所有因数求出来(不会太多,据 l j s ljs ljs 所言最多240个左右)

预处理干完了开始主要部分:

先对 a a a 数组枚举左端点在枚举 k k k 的所有因数

然后对 i i i n n n 进行二分

用之前求好的前缀和把 a i a_i ai a [ a[ a[ 二分的值 ] ] ]的和调出来看是否等于枚举的 k k k 的因数

b b b 数组同理

然后就完了^_^

上代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL x[1000005];
LL y[1000005];
LL vis[1000005];
LL sum[10005];
LL cnt;
LL a[50005];
LL b[50005];
void read(LL& x) {
    int f = 1;
    x = 0;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x *= 10;
        x += c - '0';
        c = getchar();
    }
    x *= f;
}
int main() {
    LL n, m, k;
    read(n);
    read(m);
    read(k);
    for (int i = 1; i <= k; i++) {
        if (k % i == 0) {
            sum[++cnt] = i;
        }
    }
    for (int i = 1; i <= n; i++) {
        read(a[i]);
        a[i] += a[i - 1];
    }
    for (int i = 1; i <= m; i++) {
        read(b[i]);
        b[i] += b[i - 1];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= cnt; j++) {
            LL l = i, r = n;
            LL t = 0;
            while (l < r - 1) {
                LL mid = (l + r) / 2;
                if (a[mid] - a[i - 1] == sum[j]) {
                    x[sum[j]]++;
                    t = 1;
                    break;
                } else if (a[mid] - a[i - 1] > sum[j]) {
                    r = mid - 1;
                    continue;
                } else {
                    l = mid + 1;
                    continue;
                }
            }
            if (t == 0) {
                if (a[l] - a[i - 1] == sum[j])
                    x[sum[j]]++;
                if (a[l + 1] - a[i - 1] == sum[j])
                    x[sum[j]]++;
            }
        }
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= cnt; j++) {
            LL l = i, r = n;
            LL t = 0;
            while (l < r - 1) {
                LL mid = (l + r) / 2;
                if (b[mid] - b[i - 1] == sum[j]) {
                    t = 1;
                    y[sum[j]]++;
                    break;
                } else if (b[mid] - b[i - 1] > sum[j]) {
                    r = mid - 1;
                    continue;
                } else {
                    l = mid + 1;
                    continue;
                }
            }
            if (t == 0) {
                if (b[l] - b[i - 1] == sum[j])
                    y[sum[j]]++;
                if (b[l + 1] - b[i - 1] == sum[j])
                    y[sum[j]]++;
            }
        }
    }
    LL ans = 0;
    for (int i = 1; i <= cnt; i++) {
        ans += x[sum[i]] * y[k / sum[i]];
    }
    printf("%lld", ans);
    return 0;
}

Ac代码

只有 50 50 50 分不知道哪里错了

谢谢观看

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值