【JZOJ B组】【NOIP2015模拟10.22】矩形

Description

给定一个由数字(0-9)构成的字符串s。我们可以由此定义出size(s) * size(s) 大
小的矩阵b,其中b[i][j] = s[i] * s[j];请问在这个矩阵b中,有多少子矩形满足其中的b[i][j]的和为另一个给定的数字a。

Input

第一行一个整数a。
第二行字符串s。

Output

一个整数表示满足条件的子矩形数。

Sample Input

10
12345

Sample Output

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
以上共六个。

Data Constraint

对 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);
}

你可能感兴趣的:(题解,前缀和,math)