AtCoder Beginner Contest 242(C~E)

AB 水题

C - 1111gal password

题意:给出 N(2N1e6)求满足以下条件的 X 的数量,需除以模 (998244353)

  • XN 位数
  • 对于 X1,X2,...,XN 的每位数
    • 1Xi9
    • |XiXi+1|1

思路:

emm,直接看过去知道是比较明显的数论推导问题,随便写了几组发现当定义状态 fi,j 表示长度为 i 符合条件的数字,结尾数位是 j 有多少个。递推式如下:

fi,j=fi1,j1+fi1,j+fi1,j+1fi,0=fi,10=0f1,j=1

【AC Code】

const int N = 1e6 + 10, mod = 998244353;
ll n;
ll f[N][11];
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(20);
    cin >> n;
    for (int i = 1; i <= 9; i += 1) f[1][i] = 1;
    for (int j = 2; j <= n; j++) 
        for (int i = 1; i <= 9; i++)
            f[j][i] = (f[j - 1][i - 1] + f[j - 1][i] + f[j - 1][i + 1]) % mod;
    // 定义初始和结尾以及初始值
    // {1,2,3,4,5}, accumulate(a[0], a[5],0ll) = 15
    cout << accumulate(f[n] + 1, f[n] + 10, 0ll) % mod;
}

D - ABC Transform

题意:

给定一个长度为 N 的字符串 S ,由A B C 三种字符组成,每一次变化会使 S 中的 A 全部变成 BCB 全部变成 CAC 全部变成 AB ,如 ABC 在一次变化后会变成 BCCAAB 。现在有 Q 个询问,每个询问都是求原串 S 经过 ti 次变化后第 ki 个字符

分析:

首先可以看出一个字符执行了 t 次操作以后的长度是好计算的,所以我们先把问题变成一个字符执行 t 次操作之后的第 k 个是什么。

上面的东西可以考虑作为递归做,即定义函数类似 f(c,t,k) 表示字符 c 执行了 t 次操作得到的字符串第 k 个是什么,只是存在一个问题:t(1e18) 太大了

观察到每次操作会把字符串长度加倍,所以执行很少操作后,就变成了对 f(c,t,1) 求值。然而操作三次以后第一个字符等价于没有改变,所以可以直接得到结果。

Update:发现原思路虽然正确但有点绕

f(t,k) 为原 S 经过 ti 次变化后得到的 ki 字符

首先,当 t=0 时,直接输出 Sk 即可

其次,当 t0 ,我们需要知道 f(t,k) 从何而来

样例

ABC ->
BCCAAB ->
CAABABBCBCCA

采用分类讨论的方法,不难发现

  • k2m+1|mZ (即 k 为奇数时),f(t1,k+12)f(t,k)
  • k2m|mZ (即 k 为偶数时),f(t1,k2)f(t,k)

怎么进行变化?

可以概阔为一个 g 函数:

inline char g(char ch, int x) { return (ch - 'A' + x) % 3 + 'A';}

但是,如果我们每一次都去递归 t 还是太大了,直接炸了。那么只能动 k

观察样例中字符串首,它们是 A B C 轮换的。

利用这条性质,当 k=0 即可直接求 f(t,0)=g(S.front(),tmod3)

但是问题来了,这样一个算法肯定会对 k 取模,万一 k=0...

所以,我们把 S 的改为编号 0N1

此时:

  • k2m+1|mZ (即 k 为奇数时),f(t1,k12)f(t,k)
  • k2m|mZ (即 k 为偶数时),f(t1,k2)f(t,k)

代码就很好写了

string s;
ll q, t, k;
inline char g(char ch, int x) { return (ch - 'A' + x) % 3 + 'A';}
char f(ll t, ll k) {
    if (!t) return s[k];
    if (!k) return g(s[0], t % 3);
    return g(f(t - 1, k >> 1), (k & 1) + 1);
}

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(20);
    cin >> s >> q;
    while (q --) {
        cin >> t >> k;
        cout << f(t, k - 1) << "\n";
    }
}

E - (∀x∀)

title 好玩

我们可以枚举两个字符串相同的前缀有多长,然后在后一个位置给串 X 填更小的字符,再之后的位置可以随便填了。

  • 需要判断 X 的前半段全部和 S 相同时,后半段是否小于等于 S
const int N = 1e6, mod = 998244353;
ll pw[N + 10] = {1};
string s;
int T, n;
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(20);
    for (int i = 1; i <= N; i += 1) pw[i] = pw[i - 1] * 26ll % mod;
    cin >> T;
    while (T--) {
        cin >> n >> s;
        s = "@" + s;
        int ans = 0;
        bool f = 1;
        for (int i = 1; i <= (n + 1) / 2; i++) {
            int a = s[i] - 'A', b = s[n + 1 - i] - 'A';
            ans = (ans + (ll)a * pw[(n + 1) / 2 - i]) % mod;
            f &= a <= b;
            f |= a < b;
        }
        cout << (ans + f) % mod << "\n";
    }
}
posted @   RioTian  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)
历史上的今天:
2020-03-07 PTA | 1020. 月饼 (25)
2020-03-07 PTA | 1019 数字黑洞 (20分)
2020-03-07 PTA | 1016 部分A+B (15分)
2020-03-07 LeetCode | 面试题59 - II. 队列的最大值
点击右上角即可分享
微信分享提示

📖目录