题解 [AGC020E] Encoding Subsets

传送门

先考虑怎么对一个给定 01 串计算 encode 方案数
容易想到区间 DP,枚举第一个括号转移
那么令 \(f_{i, j}\)\([i, j]\ \tt encode\) 方案数,\(g_{i, j}\) 为将 \([i, j]\) 缩到一个括号里的方案数

\[f_{i, i}=g_{i, i}=1 \]

\[f_{i, j}=g_{i, j}+\sum\limits_{k=1}^{j-i}g_{i, i+k-1}f_{i+k, j} \]

\[g_{i, j}=\sum\limits_{d\mid j-i+1}f_{i, i+d-1}[d 是 s_{i, j} 的循环节] \]

然后考虑对全部子集求
修改定义为子集的方案数,尝试用同样的方法进行转移
f 的转移是相同的
发现 g 转移时需要求出 \(f(若干个串的按位与)\)
那么 f 的参数就不能是下标而必须是确定的串了
这样的复杂度看起来比较离谱,但官方题解证明了其上界
大致思路是以 2 次操作为界
操作次数更多的话串长就缩小到可以接受的级别了
否则得到的所有串可以巧妙地约束到 \(O(n^3)\) 级别
image

所以最终复杂度大约是 \(O(n^5+2^{\frac{n}{8}})\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define pb push_back
#define ll long long
//#define int long long

int n;
string s;
vector<int> divd[N];
map<string, ll> F, G;
const ll mod=998244353;

ll g(string s);

ll f(string s) {
	if (F.find(s)!=F.end()) return F[s];
	ll ans=0;
	string t1, t2;
	for (int i=0; i<s.length(); ++i) {
		t1+=s[i]; t2.clear();
		for (int j=i+1; j<s.length(); ++j) t2+=s[j];
		ans=(ans+g(t1)*f(t2))%mod;
	}
	return F[s]=ans;
}

ll g(string s) {
	if (G.find(s)!=G.end()) return G[s];
	ll ans=0;
	for (auto d:divd[s.length()]) {
		string t;
		t.resize(d);
		for (int i=0; i<d; ++i) t[i]='1';
		for (int i=0; i<s.length(); i+=d)
			for (int j=0; j<d; ++j) t[j]=min(t[j], s[i+j]);
		ans=(ans+f(t))%mod;
	}
	return G[s]=ans;
}

signed main()
{
	cin>>s;
	n=s.length();
	F[""]=1;
	F["0"]=G["0"]=1;
	F["1"]=G["1"]=2;
	for (int i=1; i<=n; ++i)
		for (int j=i<<1; j<=n; j+=i)
			divd[j].pb(i);
	cout<<f(s)<<endl;
	// cout<<F.size()<<' '<<G.size()<<endl;
	
	return 0;
}
posted @ 2022-04-26 11:06  Administrator-09  阅读(6)  评论(0编辑  收藏  举报