思路和代码大哥们都放出来了,我就讲一下为什么可以想到这种思路吧
个人分析的时候和y总思路一样
首先根据元素的定义$c_ij=a_i×b_j$
要让一块区域里的矩阵都是1,可以很容易分析出,相应的$a_i$和$b_j$都是 连续的值都为1的子序列
问题就转为求序列a和序列b中连续的值都为1的子序列长度有多少种可能.
以求序列a为例,我们来看一下如何求序列a中长度为k的连续值为1的子序列有多少个
例如:1 1 1 0 1
问题就转化为求a中每段连续的值为1的子序列长度都是多少
例如:下标扫描到3的时候,当前$a[i]=1$,又可以轻松求得以$a[i]$为结尾的最大连续子序列长度为3,故序列a中长度为1,2,3的连续子序列数量都加上1.
扫描到末尾就能统计出来,序列a中长度为k的子序列各有多少个
注意!扫描到$a[i]$的最大连续子序列长度k后,仅需让长度小于k的子序列数量各加1就可以了!!
一开始我的想法是开个哈希表,然后扫描到长度k后遍历1-k并各加1,后来发现这就是差分的模板题,可以在线性时间内求得把某个区间(在本题中的体现为1-k)都加上1后,最后的数组情况.
所以y总的代码里做了两遍work函数,就是求差分
最后很容易就能想到,求一下k的约数,然后在对应约数i里让答案加上$S_ij$
$S_ij=$序列a中长度为i的子序列个数 * 序列b中长度为j的子序列个数
且满足$i*j=k$
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N=40010;
typedef long long ll;
int a[N],b[N];
int s1[N], s2[N];
void work(int w[], int s[], int n)
{
for (int i = 1, j = 0; i <=n; i ++ )
if (w[i])
{
j ++ ;
s[1] ++, s[j + 1] -- ;
}
else j = 0;
for (int i = 1; i <= n; i ++ ) s[i] += s[i - 1];
}
int main(){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d",&b[i]);
work(a,s1,n);
work(b,s2,m);
ll res=0;
for(int i=1;i<=n;i++){
if(k%i==0){
if(i>n) continue;
if(k/i>m) continue;
res+=(ll)s1[i]*s2[k/i];
}
}
cout<<res;
return 0;
}