• Codeforces 364A


    原题地址: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] )。

    至于接下来……就是几个需要注意的我脑残的地方了

    1. s的最大长度为4000,每一位的最大值是9,所以w最大只需要到36000,保险起见开到40000。
    2. 尽管任何一段的部分和都不会超过36000,但是w[a/q]很可能使数组越界,需要特判
    3. 如果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 }    

     继续加油!

  • 相关阅读:
    Happy New Year
    CF1450G
    理希的NOI2020退役记
    luoguP4859 已经没有什么好害怕的了(二项式反演)
    知识点简单总结——二项式反演
    bzoj4671 异或图(斯特林反演,线性基)
    知识点简单总结——斯特林数、斯特林反演
    uoj450 【集训队作业2018】复读机(生成函数,单位根反演)
    有标号DAG计数(生成函数)
    知识点简单总结——单位根反演
  • 原文地址:https://www.cnblogs.com/w007878/p/3434561.html
Copyright © 2020-2023  润新知