AGC020E Encoding Subsets
链接[https://atcoder.jp/contests/agc020/tasks/agc020_e]
简要题意
我们定义一个01
串的压缩是满足如下方式的字符串变化过程:
- 0 → 0 0\rightarrow 0 0→0, 1 → 1 1\rightarrow 1 1→1
- 如果 A → P A\rightarrow P A→P, B → Q B\rightarrow Q B→Q合法,那么 A + B → P + Q A+B\rightarrow P+Q A+B→P+Q也合法(其中+代表字符串拼接)
- 如果S= A + A + ⋯ + A ⏟ n 个 ( n ≥ 2 ) \underbrace{A+A+\cdots+A}_{n\text{个}(n\ge 2)} n个(n≥2) A+A+⋯+A,那么 S → ( A × n ) S\rightarrow(A\times n) S→(A×n)也合法(其中 ( ( (, ) ) ), × \times ×为字符, n n n为数字,算作一个字符,即使其中有 0 / 1 0/1 0/1)
我们同时定义01
串
B
B
B是
A
A
A的子集当且仅当:
- ∣ A ∣ = ∣ B ∣ |A|=|B| ∣A∣=∣B∣
- ∀ B i = 1 , A i = 1 \forall B_i=1,A_i=1 ∀Bi=1,Ai=1
现在给你一个01
串
S
S
S,问它所有的子集的合法变化结果数的总和为多少
答案对 998244353 998244353 998244353取模
∣ S ∣ ≤ 100 |S|\leq100 ∣S∣≤100
题解
直接记忆化搜索
设 F ( s t r ) F(str) F(str)为字符串 s t r str str的所有子集变化总数
设 G ( s t r ) G(str) G(str)为字符串 s t r str str的所有子集变化总数,且其子集压缩后不可再分割(即两端为括号或 s t r str str长度<=1)
- 对于 F ( s t r ) F(str) F(str):
可以枚举一个前缀使其为一段不可再分的部分,剩下的部分再继续分
即 ∑ G ( s t r [ 1 , i ] ) ∗ F ( s t r [ i + 1 , l e n ] ) \sum G(str[1, i]) * F(str[i + 1, len]) ∑G(str[1,i])∗F(str[i+1,len])
- 对于 G ( s t r ) G(str) G(str):
可以枚举一段循环节,然后把 s t r str str压缩为这一段
可以发现因为求的是子集,必须保证每一段都能取到某个最大字符串 T T T,所以枚举循环节长度(为 s t r str str长度的约数),把所有段and起来即为 T T T
即 ∑ F ( T ) \sum F(T) ∑F(T)
那么用个map记一下DP值,搜就完事了
关于状态数:
感性理解一下,产生变化的部分只有可能是 G G G中的分段and
而这个段数少时,and出来的结果就少
段数多时,就算直接拉满 2 l 2^l 2l种结果也不大( l l l为每段的长度)
实测总状态数是没有过100000的
5s随便就过了
代码
#include <cstdio>
#include <algorithm>
#include <climits>
#include <cstring>
#include <iostream>
// #include <ctime>
// #include <cmath>
#include <map>
// #include <vector>
// #include <set>
#include <string>
#define open_in(x) freopen(#x".in", "r", stdin)
#define open_out(x) freopen(#x".out", "w", stdout)
#define open_(x) freopen(#x".in", "w", stdout)
#define open(x) open_in(x); open_out(x)
#define fo(x, y, z) for (int (x) = (y); (x) <= (z); (x)++)
#define fd(x, y, z) for (int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N = -1;
const int M = 998244353;
// inline int Random(int a, int b) {return rand() % (b - a + 1) + a;}
template<class T>
inline T read(T &x) {
int f = 1; char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x *= f;
}
string S;
map<string, ll> f, g;
char And(char a, char b) {return (a == '1' && b == '1' ? '1' : '0');}
ll G(string str);
ll F(string str) {
if (f.count(str)) return f[str];
ll &v = f[str];
int len = str.length();
fo(i, 1, len)
(v += G(str.substr(0, i)) * F(str.substr(i)) % M) %= M;
// cout << 'F' << ' ' << str << ' ' << v << endl;
return v;
}
ll G(string str) {
if (g.count(str)) return g[str];
ll &v = g[str];
int len = str.length();
for (int i = 1; i * i <= len; i++)
if (len % i == 0) {
string s(i, '1');
for (int j = 0; j < len; j += i) {
fo(k, 0, i - 1)
s[k] = And(str[j + k], s[k]);
}
(v += F(s)) %= M;
if (!(i * i == len || i == 1)) {
int i2 = len / i;
s = string(i2, '1');
for (int j = 0; j < len; j += i2) {
fo(k, 0, i2 - 1)
s[k] = And(str[j + k], s[k]);
}
(v += F(s)) %= M;
}
}
// cout << 'G' << ' ' << str << ' ' << v << endl;
return v;
}
int main() {
// open_in(AT3859);
cin >> S;
f[""] = g[""] = 1;
f["0"] = g["0"] = 1;
f["1"] = g["1"] = 2;
printf("%lld\n", F(S));
// printf("%d %d\n", f.size(), g.size());
return 0;
}