Page Top

[The 2023 ICPC AROC I] I Pa?sWorD 题解

[The 2023 ICPC AROC I] I Pa?sWorD 题解

思路

朴素思路

最朴素的思路其实很简单:

\(f(\mathit{pos},\mathit{last},0/1,0/1,0/1)\) 表示 \(\mathit{pos}\) 位置,上一个为 \(\mathit{last}\),是否有大写字母、小写字母、数字。

用类似数位 DP 的转移方式,记忆化搜索即可。

代码:

#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using pii = pair<int, int>;

const ll MOD = 998244353;

#define gor(i, x) for (int i = 0; i < (x); ++i)

namespace pri {
    const int to[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
using pri::from, pri::to;

const int N = 1e5 + 10;
const int D = 65;

ll f[N][D][2][2][2];

inline void init(int s0) {
    if (islower(s0)) f[0][to[s0]][1][0][0] = 1, f[0][to[s0] + 26][0][1][0] = 1;
    else if (isupper(s0)) f[0][to[s0]][0][1][0] = 1;
    else if (isdigit(s0)) f[0][to[s0]][0][0][1] = 1;
    else {
        gor(i, 26) f[0][to[i + 'a']][1][0][0] = 1;
        gor(i, 26) f[0][to[i + 'A']][0][1][0] = 1;
        gor(i, 10) f[0][to[i + '0']][0][0][1] = 1;
    }
}

inline void do_upper(int pos, int ths) {
    auto &ff = f[pos][ths]; auto &f0 = f[pos - 1];
    gor(a, 2) gor(c, 2) gor(j, 62) if (j != ths)
    (ff[a][1][c] += (f0[j][a][0][c] + f0[j][a][1][c]) % MOD) %= MOD;
}

inline void do_lower(int pos, int ths) {
    auto &ff = f[pos][ths]; auto &f0 = f[pos - 1];
    gor(b, 2) gor(c, 2) gor(j, 62) if (j != ths)
    (ff[1][b][c] += (f0[j][0][b][c] + f0[j][1][b][c]) % MOD) %= MOD;
}

inline void do_digit(int pos, int ths) {
    auto &ff = f[pos][ths]; auto &f0 = f[pos - 1]; 
    gor(a, 2) gor(b, 2) gor(j, 62) if (j != ths)
    (ff[a][b][1] += (f0[j][a][b][0] + f0[j][a][b][1]) % MOD) %= MOD;
}

inline void do_all(int pos, int sp) {
    auto &ths = to[sp];
    if (islower(sp)) do_lower(pos, ths), do_upper(pos, ths + 26);
    else if (isupper(sp)) do_upper(pos, ths);
    else if (isdigit(sp)) do_digit(pos, ths);
    else {
        gor(i, 26) do_lower(pos, i);
        gor(i, 26) do_upper(pos, i + 26);
        gor(i, 10) do_digit(pos, i + 52);
    }
}

ll solve(int n, string str) {
    init(str[0]);
    for (int pos = 1; pos < n; ++pos) do_all(pos, str[pos]);
    ll res = 0; auto &fn = f[n - 1];
    gor(i, 62) (res += fn[i][1][1][1]) %= MOD;
    return res;
}

#define fastio() ios::sync_with_stdio(false), cin.tie(nullptr)

signed main() {
    fastio(); int n; cin >> n;
    string str; cin >> str;
    printf("%lld\n", solve(n, str));
    return 0;
}

数组开到了 \(10^5\times62\times8\),内存超限了,考虑滚动数组优化。

滚动数组

挺简单的,代码:

#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using pii = pair<int, int>;

const ll MOD = 998244353;

#define gor(i, x) for (int i = 0; i < (x); ++i)

namespace pri {
    const int to[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
using pri::from;
using pri::to;

const int N = 1e5 + 10;
const int D = 65;

ll f[2][D][2][2][2];

inline void init(int s0) {
    if (islower(s0)) f[0][to[s0]][1][0][0] = 1, f[0][to[s0] + 26][0][1][0] = 1;
    else if (isupper(s0)) f[0][to[s0]][0][1][0] = 1;
    else if (isdigit(s0)) f[0][to[s0]][0][0][1] = 1;
    else {
        gor(i, 26) f[0][to[i + 'a']][1][0][0] = 1;
        gor(i, 26) f[0][to[i + 'A']][0][1][0] = 1;
        gor(i, 10) f[0][to[i + '0']][0][0][1] = 1;
    }
}

#define ff f[pos][now]
#define f0 f[pos ^ 1]
inline void do_lower(int pos, int now) {
    gor(b, 2) gor(c, 2) gor(j, 62) if (j != now)
    (ff[1][b][c] += (f0[j][0][b][c] + f0[j][1][b][c]) % MOD) %= MOD;
} inline void do_upper(int pos, int now) {
    gor(a, 2) gor(c, 2) gor(j, 62) if (j != now)
    (ff[a][1][c] += (f0[j][a][0][c] + f0[j][a][1][c]) % MOD) %= MOD;
} inline void do_digit(int pos, int now) {
    gor(a, 2) gor(b, 2) gor(j, 62) if (j != now)
    (ff[a][b][1] += (f0[j][a][b][0] + f0[j][a][b][1]) % MOD) %= MOD;
}
#undef ff
#undef f0

#define ths to[sp]
inline void do_all(int pos, int sp) {
    memset(f[pos], 0, sizeof f[pos]);
    if (islower(sp)) do_lower(pos, ths), do_upper(pos, ths + 26);
    else if (isupper(sp)) do_upper(pos, ths);
    else if (isdigit(sp)) do_digit(pos, ths);
    else {
        gor(i, 26) do_lower(pos, i);
        gor(i, 26) do_upper(pos, i + 26);
        gor(i, 10) do_digit(pos, i + 52);
    }
}
#undef ths

ll solve(int n, string str) {
    init(str[0]); int j = 1;
    for (int pos = 1; pos < n; ++pos, j ^= 1) do_all(j, str[pos]); 
    ll res = 0; gor(i, 62) (res += f[j ^ 1][i][1][1][1]) %= MOD;
    return res;
}

#define fastio() ios::sync_with_stdio(false), cin.tie(nullptr)

signed main() {
    fastio(); int n; cin >> n;
    string str; cin >> str;
    printf("%lld\n", solve(n, str));
    return 0;
}

但是,时间复杂度呢?可知,总字符数 \(3\times10^5\),可用字符集大小 \(62\),总状态数 \(1.248\times10^8\),不可过。

优化

展开一下转移:

\[\begin{array}{l} f(\mathit{pos},\mathit{this},a,b,c):\\\quad a\rightarrow[\texttt{a},\texttt{z}],b\rightarrow[\texttt{A},\texttt{Z}],c\rightarrow[\texttt{0},\texttt{9}]\\\\ \mathit{if}\space s_{\mathit{pos}}\in[\texttt{a},\texttt{z}]\rightarrow(a=1):\\\quad f(\mathit{pos},\mathit{this},1,b,c)=\sum_jf(\mathit{pos}-1,j,0/1,b,c)-f(\mathit{pos}-1,\mathit{this},0/1,b,c)\\\\ \mathit{if}\space s_{\mathit{pos}}\in[\texttt{A},\texttt{Z}]\rightarrow(b=1):\\\quad f(\mathit{pos},\mathit{this},a,1,c)=\sum_jf(\mathit{pos}-1,j,a,0/1,c)-f(\mathit{pos}-1,\mathit{this},a,0/1,c)\\\\ \mathit{if}\space s_{\mathit{pos}}\in[\texttt{0},\texttt{1}]\rightarrow(c=1):\\\quad f(\mathit{pos},\mathit{this},a,b,1)=\sum_jf(\mathit{pos}-1,j,a,b,0/1)-f(\mathit{pos}-1,\mathit{this},a,b,0/1)\\\\ \mathit{if}\space s_{\mathit{pos}}=\texttt{?}:\\ \quad\Rightarrow[\texttt{a},\texttt{z}]:\sum_{i\in[\texttt{a},\texttt{z}]}\Big(\sum_jf(\mathit{pos}-1,j,0/1,b,c)-f(\mathit{pos}-1,i,0/1,b,c)\Big)\\ \quad\Rightarrow[\texttt{A},\texttt{Z}]:\sum_{i\in[\texttt{A},\texttt{Z}]}\Big(\sum_jf(\mathit{pos}-1,j,a,0/1,c)-f(\mathit{pos}-1,i,a,0,1,c)\Big)\\ \quad\Rightarrow[\texttt{0},\texttt{9}]:\sum_{i\in[\texttt{0},\texttt{9}]}\Big(\sum_jf(\mathit{pos}-1,j,a,b,0/1)-f(\mathit{pos}-1,i,a,b,0/1)\Big) \end{array} \]

其实就是,对于每一个 ?,都需要从上一维的每一个(排除三个情况)状态转移,那我们可以记录上一维的总和,转移的时候减去特殊的三个即可。

代码

image

posted @ 2023-10-27 18:51  RainPPR  阅读(122)  评论(0编辑  收藏  举报