【Codeforces Round #626 (Div. 2) 】B.Count Subrectangles

B. Count Subrectangles

time limit per test1 second
memory limit per test512 megabytes
inputstandard input
outputstandard output
You are given an array a of length n and array b of length m both consisting of only integers 0 and 1. Consider a matrix c of size n×m formed by following rule: ci,j=ai⋅bj (i.e. ai multiplied by bj). It’s easy to see that c consists of only zeroes and ones too.

How many subrectangles of size (area) k consisting only of ones are there in c?

A subrectangle is an intersection of a consecutive (subsequent) segment of rows and a consecutive (subsequent) segment of columns. I.e. consider four integers x1,x2,y1,y2 (1≤x1≤x2≤n, 1≤y1≤y2≤m) a subrectangle c[x1…x2][y1…y2] is an intersection of the rows x1,x1+1,x1+2,…,x2 and the columns y1,y1+1,y1+2,…,y2.

The size (area) of a subrectangle is the total number of cells in it.

Input

The first line contains three integers n, m and k (1≤n,m≤40000,1≤k≤n⋅m), length of array a, length of array b and required size of subrectangles.

The second line contains n integers a1,a2,…,an (0≤ai≤1), elements of a.

The third line contains m integers b1,b2,…,bm (0≤bi≤1), elements of b.

Output

Output single integer — the number of subrectangles of c with size (area) k consisting only of ones.

Examples
input
3 3 2
1 0 1
1 1 1
output
4
input
3 5 4
1 1 1
1 1 1 1 1
output
14

题意:
输入n,m,k,第二行输入一个n1的列向量a,第二行输入一个1m的行向量b,得到矩阵C = a·b(n x m),求C中全为1且面积为k的矩形有多少个。

思路:
(1≤n,m≤40000,1≤k≤n⋅m), 因为范围比较大,所以不能直接c矩阵,再统计。对于一个全为1的行列构成的矩形,要想面积为k,设k = a * b,则需要有连续a个b列都为1。我们可以把a的连续1的子串和b的连续1的子串分别统计出来。
比如样例1:
a = [1,0,1]T
b = [1,1,1]
a
连续1个1的个数为2,
b
连续1个1的个数为3,
连续2个1的个数为2,
连续3个1的个数为1,
要想面积为2,可以a提供1行为1的,b提供2列连续为1的,而a中能提供2个这样的1,b中能提供2个这样的2,所以2*2=4就是答案。

扫描二维码关注公众号,回复: 12419913 查看本文章

为了降低复杂度。我们可以对k进行分解因数,令k = x * y,a提供x行连续为1的,b提供y列连续为1的。

统计连续为1的长度的技巧类似最长上升子串
比如对于a

    memset(dp, 0, sizeof(dp));
    for(int i = 1; i<= n; i++) {
    
    
        if(a[i] == 1) dp[i] = dp[i-1] + 1;
        else dp[i] = 0;
    }

然后统计每一个长度连续子串出现的次数,并标记某个长度是否出现过

for(int i = 1; i <= n; i++) cnt[dp[i]]++, vis[dp[i]] = 1;

dp[i]就存了到第i位a的连续为1子串的长度。
但是对于类似 1 1 1,3个连续为1,也有2个连续为1的,2个连续为1的已经在dp[2]处记录过一次了,但记录的是1-2的,而2-3也有一段2个连续为1的子串。如果有3个连续为1的子串x个,那也一定包含x个2个连续为1的子串,所以对于每一个cnt[i]其实都少记录了cnt[i+1]次。因此从最大值开始回滚。

for(int i = maxn - 5; i >= 0; i--) cnt[i] += cnt[i + 1];

这样cnt[i]记录的就是长度为i的连续1子串的个数。

AC代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn  = 5e5 + 5;
ll a[maxn], b[maxn];
ll cnta[maxn], cntb[maxn];
bool visa[maxn], visb[maxn];
ll dp[maxn];
void getsub(ll *arr, int n, ll *cnt, bool *vis) {
    
    
    memset(dp, 0, sizeof(dp));
    for(int i = 1; i<= n; i++) {
    
    
        if(arr[i] == 1) dp[i] = dp[i-1] + 1;
        else dp[i] = 0;
    }

    for(int i = 1; i <= n; i++) cnt[dp[i]]++, vis[dp[i]] = 1;
    for(int i = maxn - 5; i >= 0; i--) cnt[i] += cnt[i + 1];
}

int main() {
    
    
    int n, m, k;

    while(scanf("%d%d%d", &n, &m, &k) == 3) {
    
    
        memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b));
        memset(cnta, 0, sizeof(cnta)); memset(cntb, 0, sizeof(cntb));
        memset(visa, 0, sizeof(visa)); memset(visb, 0, sizeof(visb));
        vector<ll> fc;
        for(int i = 1; i * i <= k; i++)
            if(!(k % i)) {
    
    
                if(i * i != k) {
    
    
                    fc.push_back(i);
                    fc.push_back(k / i);
                } else {
    
    
                    fc.push_back(i);
                }
            }

        for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        for(int i = 1; i <= m; i++) scanf("%lld", &b[i]);

        getsub(a, n, cnta, visa);
        getsub(b, m, cntb, visb);

        ll ans = 0;
        for(int i = 0; i < fc.size(); i++) {
    
    
            if(fc[i] > maxn -2 || k / fc[i] > maxn - 2) continue; //这个地方是个坑,容易RE

            if(visa[fc[i]] && visb[k/fc[i]])
                ans += cnta[fc[i]] * cntb[k/fc[i]];
        }

        printf("%lld\n", ans);
    }

    return 0;
}

赛后总结

这一场总体而言不难,但被这个B在一些细节上卡住了,写掉了i*i,T了3发,RE了4发。其余题目有空再更,因为对这个题目印象深刻所以先放上去了。

以后补题和更题主要在个人博客 baolar.com //虽然也就是写给自己看

猜你喜欢

转载自blog.csdn.net/weixin_43269437/article/details/104725099
今日推荐