给定一个由数字(0-9)构成的字符串s。我们可以由此定义出size(s) * size(s) 大
小的矩阵b,其中b[i][j] = s[i] * s[j];请问在这个矩阵b中,有多少子矩形满足其中的b[i][j]的和为另一个给定的数字a。
第一行一个整数a。
第二行字符串s。
一个整数表示满足条件的子矩形数。
10
12345
6
【样例解释】
b 矩阵为:
01 02 03 04 05
02 04 06 08 10
03 06 09 12 15
04 08 12 16 20
05 10 15 20 25
和为 10 的子矩形有:
一、01 02 03 04
二、
01
02
03
04
三、04 06
四、
04
06
五、10
六、10
以上共六个。
对 10%的输入数据:size(s)≤10
对30%的输入数据:size(s)≤100
对100%的输入数据:0 ≤a≤1000000000,size(s)≤4000
首先我们观察矩阵,根据矩阵的生成可得,第i行到第j行的和为a[i–j]*a[1–n],并且矩阵对称。
那么我们可以给a数组做一个前缀和,就可以得到所有子矩阵的和。用f数组记下每一个值有多少个。
时间复杂度O(length(f)^2)
可是,0 ≤a≤1000000000,数组开不下,也会TLE啊!
但我们发现,很多答案都是没贡献的。
其实,我们可以开两个数组,长度为sqrt(a)
c1[i]代表当n^i==0时有多少个i
c2[i]代表当n^(n/i)==0时有多少个n/i
答案就等于sigma(c1*c2)
但是,我们没有判断a=0的情况
当a=0时,ans=c1[0]*c2[0]*2-sqr(零的个数)
#include
#include
#include
#include
using namespace std;
const int maxn=4077,A=100077;
long long a[maxn],n,c1[A],c2[A],ans=0,ass=0;
//char s[maxn];
int main()
{
scanf("%lld\n",&n);
int l=0;
while(1)
{
char c=getchar();
if(c<48||c>57) break;
a[++l]=a[l-1]+c-48;
}
for(int i=0; i<=l-1; i++) for(int j=i+1; j<=l; j++)
{
if(a[j]-a[i]!=0)
{
if((a[j]-a[i])<=sqrt(n)&&n%(a[j]-a[i])==0) c1[a[j]-a[i]]++;
if((a[j]-a[i])>=sqrt(n)&&n%(a[j]-a[i])==0) c2[n/(a[j]-a[i])]++;
}else
{
c1[0]++; c2[0]++; ass++;
}
}
for(int i=1; i<=sqrt(n); i++)
{
long long s;
if(n%i==0)
{
s=c1[i]*c2[i];
if(n/i!=i) ans+=2*s;else ans+=s;
}
}
if(n==0) printf("%lld",c1[0]*c2[0]*2-ass*ass);else
printf("%lld",ans);
}