[atAGC020E]Encoding Subsets
令$f_{S}$表示字符串$S$的答案(所有子集的方案数之和),考虑转移:
1.最后是一个字符串,不妨仅考虑最后一个字符,即$f_{S[1,|S|)}$(字符串下标从1开始),特别的,若$S_{|S|}=1$,还有一个2倍的系数
2.是一个乘法,考虑是$k\times T$,记$l=|T|$,则$T$需要是末尾$k$段长为$l$的串的公共子集,不难发现这个公共子集就是这$k$个串求and后的串$T'$的子集,那么贡献即为$f_{T'}\cdot f_{S[1,|S|-kl]}$
对其记忆化搜索即可,以下来证明状态数:
考虑$f_{S[1,|S|-kl]}$这个状态,一定会通过第一种若干次后得到,因此不需要考虑
接下来,构造一棵搜索树,但这棵搜索树的每一个儿子是父亲的一个长为$kl$的子串($k\ge 2$)的$k$段字符串求and后的结果,$S$为第一层
对于四层即以后的字符串,长度一定不超过$\lfloor\frac{n}{8}\rfloor$,即至多$o(2^{\lfloor\frac{n}{8}\rfloor})$个
对于前三层的字符串,显然只需要统计第三层就足够了
当其中某一层的$k\ge 3$时,那么这一个串长度不超过$\lfloor\frac{n}{6}\rfloor$,类似的总量为$o(2^{\lfloor\frac{n}{6}\rfloor})$,也可以接受
接下来,每一层的$k$都为2,之后假设其父亲长度为$l_{1}$,自己的长度为$l_{2}$,其对应于$S$中,即$S$中的4个长为$l_{2}$的串的and,且第1和2个串相连、第3和4个串相连
因此这个字符串仅取决于第一个起点、第二个起点以及串长,总量为$o(n^{3})$
总复杂度约为$o(2^{\lfloor\frac{n}{6}\rfloor}+n^{3})$,实际上分析仍有很大的改善空间,即跑不满
具体记忆化的实现哈希+map即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 105 4 #define mod 998244353 5 map<int,int>f[N]; 6 char s[N]; 7 int get_hash(int l,char *s){ 8 int ans=0; 9 for(int i=0;i<l;i++)ans=(3LL*ans+s[i]-'0')%mod; 10 return ans; 11 } 12 int dfs(int l,char *s){ 13 if (!l)return 1; 14 int h=get_hash(l,s); 15 if (f[l][h])return f[l][h]; 16 int ans=(1+s[l-1]-'0')*dfs(l-1,s)%mod; 17 char t[N]; 18 for(int i=1;i<=l/2;i++){ 19 for(int k=0;k<i;k++)t[k]=s[l-i+k]; 20 for(int j=2;i*j<=l;j++){ 21 for(int k=0;k<i;k++)t[k]=min(t[k],s[l-i*j+k]); 22 ans=(ans+1LL*dfs(l-i*j,s)*dfs(i,t))%mod; 23 } 24 } 25 return f[l][h]=ans; 26 } 27 int main(){ 28 scanf("%s",s); 29 printf("%d",dfs(strlen(s),s)); 30 }