原题地址:http://codeforces.com/problemset/problem/364/A
题目大意:
给定一个数字a(0 ≤ a ≤ 109)和一个数列s(每个数都是一位,长度不超过4000),定义一个矩阵Mij = si * sj ,求M有多少个子矩阵上面的数字和恰巧等于a
算法分析:
这道题是Codeforces Round #213 Div 1 Problem A && Div 2 Problem C,赛场上没写对,主要是没分析清楚,有一点想法就迫不及待地提交,结果白白提交了九次。后来以为自己想通了一个小BUG,转天又开始毛毛躁躁……看了数据才发现自己脑残忘了一种情况……教训十分沉重。
首先我们定义一个子矩阵为(x, y, z, t),意思为以矩阵中的点(x, y)为左上角,(z, t)为右下角的子矩形。我们需要通过观察发现对于某个子矩形上的元素和,恰巧等于
( sumlimits_{i=x}^{z} s_{i} * sumlimits_{i=y}^{t} s_{i}) (这一点很容易证明)。接下来我们要做的就是预处理出来一个部分和( sum[i] = sumlimits_{x=1}^{i} s_{x} ),
然后我们就可以用( n^2 )的时间求出s上任意一段的部分和。设w[t]为t在所有的部分和中出现的次数(亦即:枚举i和j(i ≤ j),若i到j的部分和为t,++w[t])
那么,我们要求的就是对于a的每个因子q,( sumlimits_{q | a} w[q] * w[a / q] )。
至于接下来……就是几个需要注意的我脑残的地方了
- s的最大长度为4000,每一位的最大值是9,所以w最大只需要到36000,保险起见开到40000。
- 尽管任何一段的部分和都不会超过36000,但是w[a/q]很可能使数组越界,需要特判
- 如果a=0,需要进行特殊处理
1 //date 20131119 2 #include <cstdio> 3 #include <string> 4 #include <iostream> 5 #include <cstring> 6 7 using namespace std; 8 9 const int maxn = 4050; 10 const int maxa = 50000; 11 12 long long a, ans; 13 string s; 14 int sum[maxn]; 15 int w1[maxa]; 16 17 int main() 18 { 19 //freopen("matrix.in", "r", stdin); 20 //freopen("matrix.out", "w", stdout); 21 cin >> a; 22 ans = 0; 23 24 //if(a == 0L){printf("0 "); return 0;} 25 26 cin >> s; 27 int l = s.length(); 28 for(int i = 1; i <= l; ++i) sum[i] = s[i - 1] - '0'; 29 for(int i = 1; i <= l; ++i) 30 sum[i] = sum[i - 1] + sum[i]; 31 32 for(int i = 1; i <= l; ++i) 33 for(int j = 1; j <= i; ++j) 34 w1[sum[i] - sum[j - 1]]++; 35 36 37 if(a > 0) 38 for(int i = 1; i < maxa; ++i) 39 { 40 if(a % (long long)i == 0) 41 { 42 if((a / (long long)i) >= maxa)continue; 43 ans += (long long)w1[i] * (long long)w1[a / (long long)i]; 44 } 45 } 46 47 else{ 48 for(int i = 1; i < maxa; ++i) 49 ans += (long long)w1[0] * (long long)w1[i]; 50 ans *= 2L; ans += (long long)w1[0] * (long long)w1[0]; 51 } 52 cout << ans << endl; 53 return 0; 54 }
继续加油!