[AtCoder Grand Contest 020 Problem E]Encoding Subsets(dp+记忆化搜索)

Address

https://agc020.contest.atcoder.jp/tasks/agc020_e

Solution

如果只是求 S S 的编码方案数,那么这是一个简单的区间 DP 问题。
f[l][r] 表示子串 [l,r] [ l , r ] 的编码方案数。
g[l][r] g [ l ] [ r ] 表示子串 [l,r] [ l , r ] 不可分割的编码方案数。
不可分割是指,子串 [l,r] [ l , r ] 要么长度为 1 1 ,要么被编码成 (PxK) 的形式。
容易得出边界 f[i][i]=g[i][i]=1 ,转移:

f[l][r]=g[l][r]+i=lr1g[l][i]×f[i+1][r] f [ l ] [ r ] = g [ l ] [ r ] + ∑ i = l r − 1 g [ l ] [ i ] × f [ i + 1 ] [ r ]

g[l][r]=d|(rl+1)1d<rl+1f[l][l+d1] g [ l ] [ r ] = ∑ d | ( r − l + 1 ) 1 ≤ d < r − l + 1 f [ l ] [ l + d − 1 ]

最后答案 f[1][n] f [ 1 ] [ n ]
但题目要求的是 S S 的所有子集的方案数之和。
因此稍微改一下状态: f[S] g[S] g [ S ] 表示 01 串 S S 的子集 ^&##&**&# 方案数。
边界: g[1]=01,f[]=1
转移:
f[S]=TSTg[T]×f[ST] f [ S ] = ∑ T 是 S 的 前 缀 且 T 不 为 空 串 g [ T ] × f [ S − T ]

g[S]=TSTSf[T] g [ S ] = ∑ T 是 S 的 循 环 节 且 T ≠ S f [ T ]

最后答案 f[] f [ 原 串 ]

Code

#include <map>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Step(i, a, b, x) for (i = a; i <= b; i += x)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int PYZ = 998244353;
map<string, int> f, g;
int F(string); int G(string);
int G(string s) {
    if (g[s]) return g[s]; int i, n = s.size(); if (n == 1) return g[s] = s[0] - '0' + 1;
    int j, k, res = 0; For (i, 1, n - 1) if (n % i == 0) {
        string t = ""; For (j, 1, i) {
            int r = 1; Step (k, j - 1, n - 1, i) r &= s[k] - '0';
            t += (char) (r + '0');
        }
        res = (res + F(t)) % PYZ;
    }
    return g[s] = res;
}
int F(string s) {
    if (f[s]) return f[s]; int i, n = s.size(); if (!n) return f[s] = 1;
    int res = 0; For (i, 1, n)
        res = (res + 1ll * G(s.substr(0, i)) * F(s.substr(i, n)) % PYZ) % PYZ;
    return f[s] = res;
}
int main() {
    string s; cin >> s; cout << F(s) << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值