https://www.luogu.com.cn/problem/AT_agc020_e
不知道怎么评到紫的。最多绿。
7.15 模拟赛 T5

很明显的区间 dp。
发现整个字符串都由正常的 + 循环字符构成,考虑构建俩 dp。
dp0 存该字符串子集内是周期字符串的个数,dp1 存正经输出方案数。
dp0 内枚举周期字符段,dp1 枚举分界点,分界点前的字符段只能周期实现,点后正常实现。
又因为是一环套一环的,n = 100 又很小,考虑记忆化搜索。
具体看代码,真的很简单:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL P = 998244353;
const int N = 110;
map<string, LL> dp[2];
LL solve(bool id, string s) {
if (dp[id].find(s) != dp[id].end()) {
return dp[id][s];
}
int len = s.length();
LL res = 0;
if (id) {
for (int i = 0; i < len; i ++) {
res = (res + solve(1, s.substr(0, i))
* solve(0, s.substr(i, len)) % P) % P;
// 假设原字符串"hello"
// substr(0, 3) = "hel"
// substr(i, j),即下标从 0 开始 s[i]...s[i + j - 1]
// 如果右端点超过本身长度,超出部分为空,不计入 length
// 枚举分界点,前半部分正常,后半部分强制周期
// 前半部分最多在 [0, len - 2],所以不会死循坏
// 有人可能会问了,包含最后一位字符的循环节能处理到吗?
// 呵呵,当然能,当分界点在那个周期时就可以了
}
dp[1][s] = res;
return res;
}
else {
for (int i = 1; i < len; i ++) if (len % i == 0) {
string t = "";
for (int j = 0; j < i; j ++) {
t += "1";
}
// 这几个周期段有一个是 0,所有周期这个位置就只能是 0
for (int j = 0; j < len; j += i) {
for (int k = 0; k < i; k ++) {
if (s[j + k] == '0') {
t[k] = '0';
}
}
}
res = (res + solve(1, t)) % P;
}
dp[0][s] = res;
return res;
}
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
// 初始化
dp[0][""] = dp[1][""] = 1;
dp[0]["0"] = dp[1]["0"] = 1;
dp[0]["1"] = dp[1]["1"] = 2;
string ss;
cin >> ss;
cout << solve(1, ss) << "\n";
return 0;
}