G. Maximize the Remaining String
由小写字母组成的字符串 s s s,每次从中选取重复的字母进行删除,直到该字符串中的每个字母都只出现一次。问:最终形成的字典序最大的字符串,比如 a b a b a b ababab ababab,答案为 b a ba ba。
1 ≤ l e n ( s ) ≤ 200000 1 \leq len(s) \leq 200000 1≤len(s)≤200000
题解
记 s = a 1 a 2 a 3 ⋯ a n s=a_1a_2a_3\cdots a_n s=a1a2a3⋯an,最后的字符串为 s ′ s' s′,它的首字符在 s s s中对应位置为 p p p,显然 p p p之前的字符都被删除了,并且被删除的字符在 p p p位置之后都出现过,根据这个性质,二分枚举 p p p。假设子串 a 1 a 2 ⋯ a p a_1a_2\cdots a_p a1a2⋯ap中字典序最大的字符为 a i a_i ai:
- a i > a p a_i > a_p ai>ap,则字符 a i a_i ai作为 s ′ s' s′的首字符,更新 s = a i + 1 a i + 2 a i + 3 ⋯ a n s=a_{i + 1}a_{i+2}a_{i+3}\cdots a_n s=ai+1ai+2ai+3⋯an - { a i } \{a_i\} { ai},(减号表示删除前一个字符串中所有的后一个字符)
- a i = = a p a_i == a_p ai==ap,则字符 a i a_i ai作为 s ′ s' s′的首字符,更新 s = a p + 1 a p + 2 a p + 3 ⋯ a n s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n s=ap+1ap+2ap+3⋯an - { a p } \{a_p\} { ap}
- a i < a p a_i < a_p ai<ap,则字符 a p a_p ap作为 s ′ s' s′的首字符,更新 s = a p + 1 a p + 2 a p + 3 ⋯ a n s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n s=ap+1ap+2ap+3⋯an - { a p } \{a_p\} { ap}
当 a i > a p a_i > a_p ai>ap时,如果有多个 a i a_i ai,显然选择第一次出现的 a i a_i ai更优
通过不断的枚举 p p p确定 s ′ s' s′中的每个字符,且枚举次数不大于26次。
复杂度 O ( 26 ∗ n ∗ l o g ( n ) ) O(26*n*log(n)) O(26∗n∗log(n))
E. K-periodic Garland
由0,1组成的字符串 s s s,如果 s s s中任意相邻字符’1’之间的距离为 K K K,则该字符串是良好的。将 ‘0’ → \rightarrow → ‘1’ (或者 ‘1’ → \rightarrow → ‘0’) 称为一次操作,问最少几次操作后,能将 s s s变成良好的?
注意:‘00’、‘0100’ 对任意的 K K K都是良好的, 1 ≤ K ≤ l e n ( s ) ≤ 1 e 6 1 \le K \le len(s) \le 1e6 1≤K≤len(s)≤1e6
题解
记最终良好的字符串为 s t r str str,考虑 s s s中从左至右第一个 ‘1’ 出现的位置,记为 p o s pos pos,显然, s t r str str中从左至右第一个 ‘1’ 出现的位置不会小于 p o s pos pos,那么可以求得以 p o s pos pos作为 s t r str str中从左至右第一个 ‘1’ 出现的位置且满足良好的最少操作次数。如果不是 p o s pos pos,那么会不会是 s s s中第一个 ‘1’ 和 第二个 ‘1’ 之间的某个位置 P P P呢?假设 s t r str str满足上述条件,以 P P P位置作为 s t r str str中第一个 ‘1’ 出现的位置,这样做的话需要 t t t 次操作,而令 s [ p o s ] → 0 s[pos] \rightarrow 0 s[pos]→0,既让 s t r str str中第二个 ‘1’ 作为第一个 ‘1’,所需要的操作次数至多为 t − 1 t - 1 t−1,故这样的 P P P显然不应该考虑。综上所述,我们可以枚举 s t r str str的第一个’1’ 出现的位置,复杂度 O ( n 2 K ) O(\frac{n^2}{K}) O(Kn2)。对数论稍微敏感一点儿就会发现对 K K K的加法将这些位置划分为了 K K K类,什么意思呢?假设 K = 3 K = 3 K=3
i i 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 |
如果计算出了 p o s = 0 pos = 0 pos=0的答案,相当于也就计算出了 p o s = 3 , 6 , 9 , 12 pos = 3,6,9,12 pos=3,6,9,12的答案。所以只需要计算 K K K次,每次的复杂度为 O ( n K ) O(\frac{n}{K}) O(Kn),故总的复杂度为 O ( n ) O(n) 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 A_{m*n} Am∗n,有 q q q次询问: x , y x,y x,y,问离 ( x , y ) (x,y) (x,y)最近的 1 的曼哈顿距离。
1 ≤ n , m ≤ 1000 1 \leq n,m \leq 1000 1≤n,m≤1000, 1 ≤ q ≤ 1 e 5 1 \leq q \leq 1e5 1≤q≤1e5
题解
加一个超级源点 s s s,与所有的 1 连接起来,然后以 s s s 作为起点,BFS遍历图的同时更新距离即可。复杂度 O ( n m ) O(nm) 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 n n的序列,每次选择一个区间 [ l , r ] [l,r] [