题解 [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)\) 级别
所以最终复杂度大约是 \(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;
}