[CodeForces]8

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 1len(s)200000

题解

s = a 1 a 2 a 3 ⋯ a n s=a_1a_2a_3\cdots a_n s=a1a2a3an,最后的字符串为 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 a1a2ap中字典序最大的字符为 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+3an - { 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+3an - { 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+3an - { 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(26nlog(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 1Klen(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 t1,故这样的 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} Amn,有 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 1n,m1000 1 ≤ q ≤ 1 e 5 1 \leq q \leq 1e5 1q1e5

题解

加一个超级源点 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] [

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值