CF1270F. Awesome Substrings

给长度为$n$的$01字符串$,求区间长度是区间$1$个数的倍数的数量。$n\leq 2*10^5$

即求$r-l=k(pre[r]-pre[l])$个数。$pre[i]$为$[1,i]$中$1$的个数。

  • $k\leq \sqrt T,r-pre[r]=l-pre[l]$暴力记录一下。
  • $k > \sqrt T$,此时$pre[r]-pre[l]=\frac{r-l}{k}< \sqrt T$。暴力枚举每一个位置$l$,然后枚举$1$的个数。然后$O(1)$计算出可行的范围。(像我这么菜就是很多$if$)。
  • 计算可行范围的时候控制$k>\sqrt T$
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;

char s[N];
int sum[N], g[N];
int cnt = 0;
int mp[N * 500];
int main()
{
scanf("%s", s + 1);
int len = strlen(s + 1);
int T = sqrt(len);
for (int i = 1; i <= len; i++)
{
sum[i] = sum[i - 1] + (s[i] == '1');
if (s[i] == '1')
g[++cnt] = i;
}
ll ans = 0;
for (int i = 1; i <= T; ++i)
{
for (int j = 0; j <= len; ++j)
ans += mp[j - sum[j] * i + i * len]++;
for (int j = 0; j <= len; ++j)
mp[j - sum[j] * i + i * len]--;
}

int fi = 1;
int num1 = len / T;
for (int i = 1; i <= len; i++)
{
while (fi <= cnt && g[fi] < i)
fi++;
if (fi > cnt)
continue;
//cout << i << "----" << endl;
for (int j = 1; j <= num1 && fi + j - 1 <= cnt; j++)
{

int r = (j + fi > cnt ? len + 1 : g[j + fi]);

int q = max(g[fi + j - 1] - i + 1, (T + 1) * j);
int p = r - (q + i - 1) - 1;
if (p < 0)
continue;
//cout << j << " " << p << " " << q << endl;
if (q % j == 0)
ans += 1 + p / j;
else
{
int se = j - q % j;
if (p >= se)
ans += 1 + (p - se) / j;
}
}
}
printf("%lld\n", ans);
}