Good Bye 2019 F. Awesome Substrings【分块暴力】

F. Awesome Substrings

time limit per test

8 seconds

memory limit per test

512 megabytes

input

standard input

output

standard output

Let's call a binary string ss awesome, if it has at least 11 symbol 1 and length of the string is divisible by the number of 1 in it. In particular, 1, 1010, 111 are awesome, but 0, 110, 01010 aren't.

You are given a binary string ss. Count the number of its awesome substrings.

A string aa is a substring of a string bb if aa can be obtained from bb by deletion of several (possibly, zero or all) characters from the beginning and several (possibly, zero or all) characters from the end.

Input

The first line contains the string ss (1≤|s|≤2000001≤|s|≤200000) consisting only of zeros and ones.

Output

Output a single number — the number of awesome substrings of ss.

Examples

input

Copy

111

output

Copy

6

input

Copy

01010

output

Copy

10

input

Copy

0000

output

Copy

0

input

Copy

1111100000

output

Copy

25

Note

In the first sample, all substrings of ss are awesome.

In the second sample, we have the following awesome substrings of ss: 1 (22 times), 01 (22 times), 10 (22 times), 010 (22 times), 1010, 0101

In the third sample, no substring is awesome.

 

 

题意:给定一个01串,求有多少个子串中1的个数可以被该子串长度整除。

分析:朴素的想一下,可以预处理一个前缀和来维护区间1个数,然后再枚举两个端点,如果i-(j-1)=(pre[i]-pre[j-1])*k那么这个区间就可以计数。

同时注意到当k变大时,区间1的数量会变少;当1的数量变多时,k值一定会变小。显而易见的,若枚举1的个数,每次枚举可以O(n)计算得到,只需要尺取一下就可以了。同样的,枚举k的值,只需要计算有多少对i-pre[i]*k=(j-1)-pre[j-1]*k就可以了。

那么再来取一下k和1的个数的关系,(pre[i]-pre[j-1])=\frac{i-(j-1)}{k}\leq \frac{n}{k},即是num1*k\leq n

那么这里设定一个大小t,1的个数小于等于t的情况可以O(tn)求出,1的个数大于t时一定有k \leq \frac{n}{t},那么此时只需要从1到n/t枚举k就可以了,把所有的i-pre[i]*k计算出来排序,再把相同的值取出来计数,但注意要舍掉num1<=t的情况,这种情况的复杂度为O(n(n/t)logn)。

那么只需要调一下t的大小让两种情况的复杂度尽可能接近就可以了。

#include <bits/stdc++.h>

using namespace std;
char s[200004];
int sum[200004];
pair<int,int>a[200004];
int main() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    int t = max(n/250,(int)sqrt(n));
    int tt =n/t;
    for (int i = 1; i <= n; ++i) {
        sum[i] = sum[i - 1] + (s[i] == '1');
    }
    long long ans = 0;
    for (int i = 1; i <= t; ++i) {
        int r = 0, rr = 0;
        int num = 0;
        for (int l = 1; l <= n; ++l) {
            while (num < i && r < n) {
                num += (s[++r] == '1');
            }
            rr =max(rr,r);
            while (rr < n && s[rr + 1] != '1')rr++;
            if (num == i)ans += (rr - l + 1) / i - (r - l) / i;
            num -= s[l] == '1';
        }
    }
    for (int i = 1; i <= tt; ++i) {
        for (int j = 1; j <= n; ++j) {
            a[j] = make_pair(j-sum[j]*i,sum[j]);
        }
        a[n+1]=make_pair(0,0);
        sort(a+1,a+1+n+1);
        for (int l = 1, r; l <= n+1; l=r+1) {
            r = l;
            while(r <= n && a[r+1].first == a[l].first)r++;
            int ll = l,rr = l;
            for (ll = l; ll <= r; ++ll) {
                while(rr <= r && a[rr].second - a[ll].second <= t)rr++;
                ans += r - rr + 1;
            }
        }
    }
    cout<<ans<<endl;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值