AGC020E Encoding Subsets

AGC020E Encoding Subsets

链接[https://atcoder.jp/contests/agc020/tasks/agc020_e]

简要题意

我们定义一个01串的压缩是满足如下方式的字符串变化过程:

  • 0 → 0 0\rightarrow 0 00, 1 → 1 1\rightarrow 1 11
  • 如果 A → P A\rightarrow P AP, B → Q B\rightarrow Q BQ合法,那么 A + B → P + Q A+B\rightarrow P+Q A+BP+Q也合法(其中+代表字符串拼接)
  • 如果S= A + A + ⋯ + A ⏟ n 个 ( n ≥ 2 ) \underbrace{A+A+\cdots+A}_{n\text{个}(n\ge 2)} n(n2) 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 S100

题解

直接记忆化搜索

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值