2019牛客多校第四场 K-number

题目链接:https://ac.nowcoder.com/acm/contest/884/K

题目描述:

300iq loves numbers who are multiple of 300.
One day he got a string consisted of numbers. He wants to know how many substrings in the string are multiples of 300 when considered as decimal integers.
Note that leading and trailing zeros are allowed (both in original string and substrings you chose) and the same substring appearing in different places can be counted multiple times.

输入描述:

A single line consisting a string consisted of characters ‘0’ to ‘9’.

输出描述:

The number of substrings that are multiples of 300 when considered as decimal integers.

示例:

输入:
600

输出:
4

说明:
‘600’, ‘0’, ‘0’, ‘00’ are multiples of 300. (Note that ‘0’ are counted twice because it appeared two times)

输入:
123000321013200987000789

输出:
55

解题思路:

题意就是让你求数字串中是300倍数的字串个数,这里很容易想到可以把题目理解为寻找同时为3的倍数和100的倍数的字串并计数。

100的倍数结尾至少有两个0,所以要找串中一堆一堆的连续的0,答案就分布在这些地方。

而3的倍数有个特点:各个位上数字加起来为3的倍数,结果的各个位再加起来依然是,加到最后可以得出一个3。这样,我们作一个前缀和处理,然后每个前缀和mod 3,基于这个性质我们有一个结论:令前缀和数组为sum,若sum[a] == sum[b],则子串(a, b]为3的倍数,证明在代码之后的部分。

那么现在。我们的思路就是在串中找连续的0,由题目说明,我们知道由0组成的串都是300的倍数,所以答案先加上这堆0内部的子串个数。然后设这堆0的前一个元素为str[i],我们找有多少个预处理的前缀和 mod 3 == sum[i] mod 3,那就有多少个已str[i]结尾的子串是3的倍数,再乘上后面这堆0中长度>=2的前缀个数,加到答案上。扫一遍就可以得出全部答案,复杂度O(n)。

AC代码:

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <vector>
#include <bitset>
using namespace std;
typedef long long ll;

const double    PI   = acos(-1.0);
const int       INF  = 0x3f3f3f3f;
const long long LINF = 1e18;
const long long MOD  = 1e9+7;
const int       MAXN = 1e5+10;

char str[MAXN];
ll cnt[3], cur = 0, ans = 0;

int main()
{
    scanf("%s", str+1);
    cnt[0] = 1;
    for(int i = 1; str[i]; i++)
    {
        ll zlen = 0;//连续的0的长度
        while(str[i] == '0') i++, zlen++;
        ans += (zlen + 1) * zlen / 2;
        if(zlen >= 2) ans += (cnt[cur] - 1) * (zlen - 1);
        cnt[cur] += zlen;
        cur = (cur + str[i] - '0') % 3;
        cnt[cur]++;
    }
    printf("%lld\n", ans);
    return 0;
}

关于前缀和 mod 3那个性质的证明:

命题:
有数字串str,str[i]的前缀和为sum[i],若(sum[a] mod 3) == (sum[b] mod 3),则子串(a, b]为3的倍数

证明:
当sum[i] mod 3 == 0时,sum[i]为3的倍数;若有sum[j] mod 3 == 0(j >= i),则sum[j]也为3的倍数,那么sum[j] - sum[i]也为3的倍数,那么子串(i, j]是3的倍数。

当sum[i] mod 3 != 0时,设sum[i] mod 3 == k, (k != 0),那么sum[i]可以表示为3a+k,若有sum[j] mod 3 == k (j >= i,k != 0),则sum[j]可表示为3b+k,那么sum[j] - sum[i] = (3b+k) - (3a+k) = 3(b-a),是3的倍数。

Q. E. D.

你可能感兴趣的:(2019牛客多校)