codeforce刷题(六)
G. Maximize the Remaining String
由小写字母组成的字符串\(s\),每次从中选取重复的字母进行删除,直到该字符串中的每个字母都只出现一次。问:最终形成的字典序最大的字符串,比如\(ababab\),答案为\(ba\)。
\(1 \leq len(s) \leq 200000\)
题解
记\(s=a_1a_2a_3\cdots a_n\),最后的字符串为\(s'\),它的首字符在\(s\)中对应位置为\(p\),显然\(p\)之前的字符都被删除了,并且被删除的字符在\(p\)位置之后都出现过,根据这个性质,二分枚举\(p\)。假设子串\(a_1a_2\cdots a_p\)中字典序最大的字符为\(a_i\):
- \(a_i > a_p\),则字符\(a_i\)作为\(s'\)的首字符,更新\(s=a_{i + 1}a_{i+2}a_{i+3}\cdots a_n\) - \(\{a_i\}\),(减号表示删除前一个字符串中所有的后一个字符)
- \(a_i == a_p\),则字符\(a_i\)作为\(s'\)的首字符,更新\(s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n\) - \(\{a_p\}\)
- \(a_i < a_p\),则字符\(a_p\)作为\(s'\)的首字符,更新\(s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n\) - \(\{a_p\}\)
当\(a_i > a_p\)时,如果有多个\(a_i\),显然选择第一次出现的\(a_i\)更优
通过不断的枚举\(p\)确定\(s'\)中的每个字符,且枚举次数不大于26次。
复杂度\(O(26*n*log(n))\)
E. K-periodic Garland
由0,1组成的字符串\(s\),如果\(s\)中任意相邻字符'1'之间的距离为\(K\),则该字符串是良好的。将 '0' \(\rightarrow\) '1' (或者 '1' \(\rightarrow\) '0') 称为一次操作,问最少几次操作后,能将\(s\)变成良好的?
注意:'00'、'0100' 对任意的\(K\)都是良好的,\(1 \le K \le len(s) \le 1e6\)
题解
记最终良好的字符串为\(str\),考虑\(s\)中从左至右第一个 '1' 出现的位置,记为\(pos\),显然,\(str\)中从左至右第一个 '1' 出现的位置不会小于\(pos\),那么可以求得以\(pos\)作为\(str\)中从左至右第一个 '1' 出现的位置且满足良好的最少操作次数。如果不是\(pos\),那么会不会是\(s\)中第一个 '1' 和 第二个 '1' 之间的某个位置\(P\)呢?假设\(str\)满足上述条件,以\(P\)位置作为\(str\)中第一个 '1' 出现的位置,这样做的话需要 \(t\) 次操作,而令\(s[pos] \rightarrow 0\),既让\(str\)中第二个 '1' 作为第一个 '1',所需要的操作次数至多为 \(t - 1\),故这样的\(P\)显然不应该考虑。综上所述,我们可以枚举\(str\)的第一个'1' 出现的位置,复杂度\(O(\frac{n^2}{K})\)。对数论稍微敏感一点儿就会发现对\(K\)的加法将这些位置划分为了\(K\)类,什么意思呢?假设\(K = 3\)
\(i\) | |||||
---|---|---|---|---|---|
0 | 3 | 6 | 9 | 12 | 15 |
1 | 4 | 7 | 10 | 13 | 16 |
2 | 5 | 8 | 11 | 14 | 17 |
3 | 6 | 9 | 12 | 15 | 18 |
4 | 7 | 10 | 13 | 16 | 19 |
如果计算出了\(pos = 0\)的答案,相当于也就计算出了\(pos = 3,6,9,12\)的答案。所以只需要计算\(K\)次,每次的复杂度为\(O(\frac{n}{K})\),故总的复杂度为\(O(n)\)。
//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,no-stack-protector,fast-math")
// cnt[j] := 以 j 位置作为 str 中第一个 ‘1’ 的位置且使得 s 变为 str 所需要的最少次数
#include <bits/stdc++.h>
#define IO ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 1e6 + 5;
int t, n, k;
int main()
{
cin >> t;
while (t--)
{
cin >> n >> k;
string s;
cin >> s;
vector<int> use(2 * n + 5, 0);
vector<int> cnt(2 * n + 5, 0);
int pos = -1;
for (int i = n - 1; ~i; i--) if (s[i] == '1') {
pos = i;
break;
}
if (pos == -1) {
cout << 0 << endl;
continue;
}
for (int i = 0; i < pos; i++) cnt[i] = -1;
use[n - 1] = (s[n - 1] == '1');
for (int i = n - 2; ~i; i--) {
if (s[i] == '1') use[i] = 1;
use[i] += use[i + 1];
}
int ans = n << 1 + 1;
for (int i = n - 1; ~i; i--) if (s[i] == '1') {
int tp = 0;
for (int j = i + k; ; j += k) {
tp += (use[j - k + 1] - use[j] + 1);
if (cnt[j] != -1) {
tp = tp - 1 + cnt[j];
if (use[i + 1]) tp = min(tp, use[i + 1]); // 直接抹掉 i 位置之后的所有的 ‘1’,可能较优。例如 K = 1,s = 10001
break;
}
if (j >= n) break;
}
cnt[i] = tp;
ans = min(ans, tp + use[0] - use[i]);
}
cout << ans << endl;
/* code */
}
return 0;
}
wonderful, BFS
给一个01矩阵\(A_{m*n}\),有\(q\)次询问:\(x,y\),问离\((x,y)\)最近的 1 的曼哈顿距离。
\(1 \leq n,m \leq 1000\),\(1 \leq q \leq 1e5\)
题解
加一个超级源点\(s\),与所有的 1 连接起来,然后以\(s\) 作为起点,BFS遍历图的同时更新距离即可。复杂度\(O(nm)\)【单纯的记录一哈,orz
queue < pair<int, int> > qe;
void BFS() {
memset(d, -1, sizeof(d));
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (mp[i][j]) {
d[i][j] = 0;
qe.push(make_pair(i, j));
}
while(!qe.empty()) {
pair<int, int> p = qe.front();
qe.pop();
for (int i = 0; i < 4; i++) {
int u = p.first + dx[i], v = p.second + dy[i];
if (u < 0 || v < 0 || u >= n || v >= m || d[u][v] != -1) continue;
d[u][v] = d[p.first][p.second] + 1;
qe.push(make_pair(u, v));
}
}
}
1349C - Orac and Game of Life【好题】
给一个长度为\(n\)的序列,每次选择一个区间\([l,r]\)并将这个区间包含的元素都替换为该区间的中位数(按照大小关系排在中间的数)。问最终是否能将整个序列中的元素都替换为\(k\)?
\(1 \leq n \leq 1e5\),\(1 \leq a_i, k \leq 1e9\)
题解
-
原始序列不包含\(k\),显然无解
-
如果原始序列中存在一个区间,其中位数为\(k\),则最终肯定能够将整个序列替换为\(k\)。
-
如果原始序列不存在上述区间呢?构造
在原序列中直接找一个中位数为\(k\)的区间,想了很久都没想到一个好的办法。其实,着眼于细微之处,如果序列中存在相邻的两个数为\(k,a_i\)或者\(a_i,k\),有\(a_i \geq k\),那么显然这是一个中位数为\(k\)的区间。如果不存在这样的相邻两个数,既对任意的\(k\),其左右相邻的两个数都小于\(k\)(\(a_i,k,a_j\),都有\(a_i < k\),\(a_j < k\)),那么只需要将其中的任意一个(\(a_i\)或者\(a_j\))替换为大于或者等于\(k\)的数就行,等价于找一个中位数大于或者等于\(k\)的区间。怎么找呢?【orz】其实只需要找到一个区间\((a_e,a_{e+1},a_{e+2})\),其中至少存在两个大于\(k\)的数就行。为什么只需要找长度为3区间呢?假设存在一个区间\([l,r]\)其中位数为\(p\),\(p\geq k\),说明这个区间大于或者等于\(p\)的元素至少有\(\frac{r-l+1}{2} + 1\)个,既大于或者等于\(k\)的数至少超过一半,显然,至少存在一个长度为3的区间其中位数必定大于或者等于\(k\)。证明的话,将区间划分成一个一个长度为3且不相交的区间,然后对最后剩下的一个区间分类讨论即可。如果找不到上述的长度为3且满足条件的区间,显然不可能将\(a_i\)或者\(a_j\)替换为大于或者等于\(k\)的数。
1349A - Orac and LCM
给一个长度为\(n\)的序列,求\(gcd(\{lcm(a_i,a_j)|1 \leq i,j \leq n,i \neq j\})\)
\(1 \leq n,a_i \leq 2e5\)
题解
想到了枚举最终答案的约数,但等于圈圈。记最终答案为\(ans\),\(p\)表示质数,注意:如果\(p^k | ans\),那么至少存在\(n-1\)个\(a_i\),有\(p^k|a_i\)。
反证法很好证明。知道这个特点之后怎么做呢?直接的思路依然是枚举,然而!求\(n-1\)个\(a_i\)的\(gcd\)不就找到了\(p^k\)吗,妙得一匹。记\(gcd(a_i) = gcd(\{a\} - \{a_i\})\),那么\(ans = lcm(gcd(a_1), gcd(a_2), gcd(a_3),\cdots,gcd(a_n))\).
1346F - Dune II: Battle For Arrakis
给一个棋盘\(A_{n*m}\),每个格子上有\(a_{ij}\)枚棋子,记\(cnt\)为 将所有的棋子移到同一个格子上所需要的最少次数,一次只能将一枚棋子移动一步(上下左右)。现给\(k\)次修改:将\(a_{ij} \rightarrow b_{ij}\),问每次修改后需要的最少次数,既\(cnt\)。
\(1 \leq n,m,k \leq 1000\)
题解
我的想法:研究修改后的最佳位置与先前最佳位置的关系,然并卵
记最佳位置为\((x,y)\),有
记\(A_i = \sum_{j=1}^ma_{ij}\), \(B_j = \sum_{i=1}^na_{ij}\),有
这是一个最优化问题,最小化\(cnt\),也就是分别最小化\(\sum_{i=1}^n|x-i|*A_i\) 和 \(\sum_{j=1}^m|y-j|*B_j\)。
重点来了,最小化\(\sum_{i=1}^n|x-i|\)比较好做,在最中间任取一个数就行。那多了一部分\(A_i\)该怎么做喃?在脑海中抽象一个模型,\(x\)轴上有一些球落在\(x_i\)处,记\(x_i\)处的球的个数为\(A_i\),然后有一个挡板\(X\)也在\(x\)轴上,问\(X\)的最佳位置,使得\(\sum_{i=1}^n|X-x_i|*A_i\)最小。本质上与最小化\(\sum_{i=1}^n|x-i|\)是同一个问题,所以\(X\)的位置依然是在最中间。同样的道理可以求得\(y\)的位置,知道\((x,y)\)后就可以以\(O(1000)\)的复杂度求得\(cnt\),最终复杂度为\(O(max(n,m)*k)\)
Palindrome(好题)
给一个字符串\(s\),问它有多少个子串是好子串?一个子串被称为好子串\(S\)当且仅当它的长度为\(3n-2\)并且\(S[i] = S[2n-i] = S[2n + i - 2]\)\((1 \leq i \leq n)\).
\(1 \leq len(s) \leq 500000\)
题解
分析\(S[i] = S[2n-i] = S[2n + i - 2]\),可以发现对于一个长度为\(3n-2\)的好子串\(S\),有:
- \(S_{123...(2n-1)}\)是一个回文串并且中心为\(n\),半径为\(n-1\);
- \(S_{n(n+1)(n+2)...(3n-2)}\)是一个回文串并且中心为\(2n-1\),半径为\(n-1\)
枚举前一个回文串的中心和半径,那么后一个回文串的中心和半径也就确定了。先用马拉车算法预处理出每个位置的回文半径\(len[pos]\)。考虑已经枚举到\(s\)的第\(i\)位置,该位置的回文半径为\(len[i]\),如果\(len[i+len[i]] \geq len[i]\),那么一个好子串就出现了,所以需要考虑以\(i\)位置为中心的所有回文子串,检查是否有合法的后一个回文串,既\(len[i + len[i] - t] \geq len[i] - t\)\((0 \leq t \leq len[i] - 1)\).
显然,不能直接暴力的检查 以\(i\)位置为中心的所有回文子串是否有合法的后一个回文串。观察上面这个不等式,移项:
重点来了,令\(p[i] = n - i + 1\),\((1\leq i \leq n)\),那么合法的后一个回文串的个数就等于在\(\sum_{j=i+1}^{i+len[i]}((len[j] + p[j]-p[i+len[i]])\geq len[i])\),移项:
求该和式等价于求一个区间\([l,r]\)内,大于或者等于\(K\)的数有多少个?方法比较多,主席树,线段树,离线树状数组。
/* 树状数组 */
int n, m;
int len[maxn << 1], b[maxn];
char s[maxn << 1], s1[maxn];
pair<int, int> a[maxn];
void change() {
for (int i = 1, t = 0; i <= n; i++) {
s[++t] = '#';
s[++t] = s1[i];
}
s[0] = '$', s[2 * n + 1] = '#', s[2 * n + 2] = '@', s[2 * n + 3] = '\0';
n = 2 * n + 3;
}
void ma() {
int id = 0;
for (int i = 1; i <= n; i++) {
int t = i > len[id] + id ? t = 0 : min(len[2 * id - i], id + len[id] - i);
while(s[i + t] == s[i - t]) t++;
len[i] = t - 1;
if (id + len[id] <= i + len[i]) {
id = i;
}
}
id = 0;
for (int i = 1; i <= n; i++) if (s[i] != '#') {
len[++id] = len[i] / 2;
}
}
struct query {
int l, r, x;
bool operator < (const query& i) const {
if (x == i.x) return l < i.l;
return x < i.x;
}
} q[maxn];
int lowbit(int x) {
return x & (-x);
}
void add(int i, int x) {
while(i <= m) {
b[i] += x;
i += lowbit(i);
}
}
int sum(int i) {
int res = 0;
while(i > 0) {
res += b[i];
i -= lowbit(i);
}
return res;
}
void solve() {
int t = 0;
for (int i = 1; i <= m; i++) if (len[i]) {
q[t].l = i + 1;
q[t].r = i + len[i];
q[t++].x = len[i] + m - i - len[i] + 1;
}
for (int i = 1; i <= m; i++) {
a[i].first = len[i] + m - i + 1;
a[i].second = i;
}
sort(a + 1, a + m + 1);
sort(q, q + t);
int j = 1;
long long res = 0;
for (int i = 0; i < t; i++) {
while(a[j].first < q[i].x && j <= m) {
add(a[j].second, 1);
j++;
}
res += (q[i].r - q[i].l + 1) - (sum(q[i].r) - sum(q[i].l - 1));
}
printf("%lld\n", res);
}
int main()
{
int cas;
cin >> cas;
while (cas--)
{
/* code */
scanf("%s", s1 + 1);
memset(len, 0, sizeof(len));
memset(b, 0, sizeof(b));
m = n = strlen(s1 + 1);
change();
ma();
solve();
}
return 0;
}
1346E - Magic Tricks
\(n\)个位置,每个位置上都有一个球。初始时,第\(k\)个位置上是一个特殊的球。现给出\(m\)次交换,记\(t_i\)表示\(m\)次交换后,特殊的球被交换到了\(i\)位置的最少 \(fake ~ swap\) 次数。一次交换被称为\(fake ~ swap\) 当且仅当这次交换什么都没做,既不发生交换。求出所有\(t_i\)\((1 \leq i \leq n)\)。
\(1 \leq n,m \leq 2e5\)
题解
时间是线性的,令\(dp[a][b]:=\) \(b\)次交换后,特殊的球被交换到\(a\)位置的最少\(fake ~ swap\)次数,有:
1345D - Monopole Magnets
给一个\(n*m\)的棋盘,每个格子都有一种颜色,黑色或者白色,然后将一些\(N\)和\(S\)放入棋盘的格子中,一个格子可以放入多个,且\(N\)和\(S\)可以放在同一个格子里面。求最少需要放入\(N\)的个数(\(S\)的个数不限)使得满足如下三个条件:
- 棋盘的每行每列至少有一个\(S\)。
- 对于黑色位置\((i,j)\),经过有限次操作后,至少存在一个\(N\)能到达这些位置。
- 对于白色位置\((i,j)\),无论经过多少次的操作,都没有\(N\)能到达这些位置。
一次操作:选取任意两个在同行或者同列,但不在同一个格子里面的\(N\)和\(S\),保持\(S\)的位置不变,\(N\)向\(S\)靠近一步。
\(1 \leq n,m \leq 1000\)
题解
先判断不存在解的情况。如果\((i,j)\)位置是白色,且这个位置放\(S\),那么第\(i\)行和第\(j\)列不能存在黑色位置,因为既然能达到黑色位置,则经过有限次操作后,必定能到达\((i,j)\),而\((i,j)\)是不可达的。如果\((i,j)\)位置是黑色,且这个位置放\(S\),那么第\(i\)行和第\(j\)列只能存在一段连续的黑色位置,因为若存在两端或者更多段黑色位置,则段与段之间白色的部分必定可达。检查完所有的位置后,如果某行或者某列所有的位置都不能放\(S\),则解不存在。
经过上面的检查后,棋盘中黑色的位置组成了一个个连通块,在每个连通块的边界都放上\(S\),显然,一个连通块只需要在块内任意位置处放入一个\(N\)就行了。故最少的\(N\)的个数就是连通块的个数。
1343D - Constant Palindrome Sum
给一个长度为\(n\)的序列\(A\),问最少需要几次操作使得\(\forall i \in [1;\frac{n}{2}]\),有\(a_i + a_{n-i+1} = X\)。
一次操作:将任意一个数置为区间\([1;k]\)内的数。
\(1 \leq a_i \leq k \leq 2e5\),\(1 \leq n \leq 2e5\),且\(n\)为偶数。
题解
记\(x = min(a_i, a_{n - i + 1})\),\(y = max(a_i, a_{n - i + 1})\)。如果\(X\)落在区间:
- \([2, x]\),第\(i\)对的贡献为2
- \([x + 1, x + y - 1]\),第\(i\)对的贡献为1
- \([x+y, x+y]\),第\(i\)对的贡献为0
- \([x + y + 1, k + y]\),第\(i\)对的贡献为1
- \([k + y + 1, 2 * k]\),第\(i\)对的贡献为2
显然,我们可以在每一对 \(i\) 所对应的这些区间上预先加上贡献,最后求区间\([2; 2*k]\)上的最小值就是最少操作次数。区间操作,单点查询,可以用树状数组。但还有比较简单的方法,利用差分:在区间\([l,r]\)上加1,则\(b[l] += 1\),\(b[r + 1] -= 1\),处理完所有的区间后,扫描一遍求前缀和,既\(b[i] += b[i - 1]\),最后\(answer = min\{b_i\}\)。
1342C - Yet Another Counting Problem
给两个正整数\(a,b\),问在区间\([L,R]\)内有多少个数满足:\((x \% a) \% b ~~!= ~~(x\%b)\%a\) ?
\(1 \leq a, b, L, R \leq 1e18\)
题解
不妨假设\(a \leq b\),如果\((x \% a) \% b ~~= ~~(x\%b)\%a\),有:\(x \% a ~~= ~~(x\%b)\%a\),并且\(a | (x - x \% b)\)。假设\(x = p * b + r\),则有\(a |(p*b)\),既\(p\)是\(\frac{a}{gcd(a,b)}\)的倍数,既\(p\)的取值为:\(\{0, \frac{a}{gcd(a,b)}, \frac{2a}{gcd(a,b)}, \frac{3a}{gcd(a,b)},... \}\),而\(r\)的取值为:\(\{0, 1, 2, 3, ...,b-1\}\)。所以区间\([0, R]\)内满足相等条件的\(x\)的个数为:\(\frac{(R+1)*gcd(a,b)}{ab} * b + min(b, (R+1)\%(\frac{ab}{gcd(a,b)}))\)。
1342D - Multiple Testcases
给一个长度为\(n\)的序列\(A\),\(1 \leq a_i \leq k\),和长度为\(k\)的序列\(C\),\(c_1 \geq c_2 \geq c_3 \geq ... \geq c_k\),问\(A\)最少能被分成几组,使得每组都满足如下条件:
- 记每组中大于或者等于\(i\) 的个数为\(g_i\),有:\(g_i \leq c_i\)。
\(1 \leq n,k \le 2e5\)
题解
记最少组数为\(ans\),有:\(ans = max\lceil \frac{g_i}{c_i} \rceil\)。怎么构造呢,令\(a_i \in ans[i \% ans]\),\(i \in {0,1,2,...,n-1}\)。还是比较巧妙得!
1341D - Nastya and Scoreboard
给\(n\)个数字显示器,问再打开\(k\)个显示器中导管后,这\(n\)个数字显示器所组成的最大整数?
\(1 \leq n, k \leq 2000\)
题解
令\(dp[i][j]:=\) \(i\) 个数字显示器再打开\(j\)个导管时,第\(i\)个数字显示器显示的最大数字。为了满足最大的整数,第一个显示器显示的数字应该尽可能的大,实际上就是\(dp[0][k]\)。从后向前递归,**记录所有合法的状态\(dp[i][j]\) **,再从前向后回溯就能找到所有合法的显示器的数字了。
string s[10] = {"1110111", "0010010", "1011101", "1011011", "0111010", "1101011", "1101111", "1010010", "1111111", "1111011"};
int get_int(string str) {
int res = 0;
for (int i = 6, j = 1; ~i; i--, j *= 2) if (str[i] == '1') res += j;
return res;
}
int main()
{
memset(dp, -1, sizeof(dp));
memset(mp, -1, sizeof(mp));
cin >> n >> k;
for (int i = 0; i < n; i++) {
string t;
cin >> t;
int x = get_int(t);
for (int j = 0; j < 10; j++) {
int y = get_int(s[j]);
if ((x & y) == x) mp[i][__builtin_popcount(x ^ y)] = j;
}
}
for (int i = 0; i < 8; i++) dp[n - 1][i] = mp[n - 1][i];
for (int i = n - 2; ~i; i--) {
for (int j = 0; j <= k; j++) if (dp[i + 1][j] >= 0){
for (int p = 0; p <= 7 && j + p <= k; p++) dp[i][j + p] = max(dp[i][j + p], mp[i][p]);
}
}
int cnt = 0;
for (int i = 0; i < n; i++) {
int u = dp[i][k - cnt];
cout << u;
if (u == -1) return 0;
for (int j = 0; j <= 7; j++) if (mp[i][j] == u) {
cnt += j;
break;
}
}
cout << endl;
return 0;
}
1338B - Edge Weight Assignment(好题:通过构造点权值来构造边权值)
给一颗树,为每条边赋一个权值(正整数),问最少和最多需要几个不同的正整数,使得任意一对叶子之间路径异或值为0,既有:\(l_1w_0v_1w_1v_2w_2...v_iw_il_2\),\(w_0 \bigoplus w_1 \bigoplus w_2 \bigoplus ... \bigoplus w_i = 0\)。
题解
最少需要1个或者3个,也就是对所有的边赋值同一个整整数,分类讨论一下即可。
为每个叶子节点赋点权值:0,为非叶节点赋点权值:\(2^i\)。则边权值为:\(w(e(i,j)) = v_i \bigoplus v_j\)。显然,任意一对叶子之间的路径异或为0,所以最多需要\(e - \sum_v(leaf(v) - 1)\)个正整数。简直妙级了!
\(leaf(v)\)表示节点\(v\)的叶儿子个数.
1336A - Linova and Kingdom
给一颗\(n\)个节点的树,根为1,从树中选取\(k\)个节点建立工厂,剩余的所有节点建立公园,问这\(k\)个工厂各自派出一个工人,这些工人走到树根最多能游玩几个公园?工人只能沿着最短路走。
\(1 \leq k \leq n \leq 2e5\)
题解
当一个节点被选择建立工厂时,它的所有子节点在之前就已经被选择建立工厂,否则具有更优的方案。所以每个节点的实际贡献为\(dep[u] - size[u]\)。求出每个点的实际贡献,排个序,从大往小选择\(k\)个贡献较大的节点。
1333C - Eugene and an array
给一个序列,问有多少个子串满足:子串的任意一个子串都是好子串 ?
- 好子串\(A_{l...r}\):\(a_l + a_{l +1} + a_{l + 2} + ... + a[r] \neq 0\)
\(1 \leq n \leq 2e5\),\(-10^9 \leq a_i \leq 10^9\)
题解
令\(R[i]\)表示以\(i\)为左端点且最短 非好子串 的右端点位置,既\(a_i + a_{i + 1} + a_{i + 2} + ... + a_{R[i]} = 0\)。当然,也可能不存在\(R[i]\)。记\(L[R[i]] = i\),那么\(i\)位置为左端点的好子串个数就是:\(R[j] - i\) 或者是 \(n - i + 1\),\(j = (argmin_jR[j] \geq i) \&\& (L[R[j]] \geq i)\)。\(j\) 的取值应该使得\(R[j]\)尽量靠近\(i\)的同时其左边界要大于等于\(i\)。
怎么求\(R[i]\)?求出所有位置的前缀和\(sum[i]\),显然有\(sum[R[i]] = sum[i-1]\),用一个二维数组记录相同\(sum\)值出现的所有位置,然后二分查找满足等式且最小的\(R[i]\)就可以了。