感覺只會抄題解。。
AGC020E
首先自己可以想到如果只是要單純的求一個串的方案數可以區間 dp:觀察這個表達式,可以寫成
f := g | g + f
g := '0' | '1' | '(' + f + '*' + 'x' + ')'
其中 x
是一個數字,要求 \(x>1\),並且 \(f\) 是 \(g\) 的一個長度爲 \(x\) 的循環節。
那麼就可以根據上面的定義寫出 dp 了:設 \(f_{l,r},g_{l,r}\) 表示區間 \([l,r]\) 的答案,根據上面的定義可以得到轉移:
接下來就看題解了。
題解裏說,我們把狀態改成 \(f_s\) 表示字符串 \(s\) 的所有子串的方案數,然後類似做就好了。這時候轉移 \(f\) 就是枚舉斷開,\(g\) 就是枚舉循環節,將所有長度爲 \(d\) 的子串的按位與拿下去做。這樣看起來十分暴力,但是我們可以推一下複雜度:設 \(T_f(n)\) 表示長度爲 \(n\) 的 \(f\) 的計算時間,\(T_g(n)\) 表示長度爲 \(n\) 的 \(g\) 計算時間,有(這裏要注意 時間是相加不是相乘):
注意這裏第一行是乘 \(n-i+1\) 而不是加 \(T_f(n-i)\) 的原因 \(f\) 不會生成新的串,只會導致 \(g\) 被多算幾遍,那麼一個 \(g\) 會被枚舉 \(n-len+1\) 次。這樣大概運算是 29310258 反正能過(
#include <bits/stdc++.h>
#define fi first
#define se second
#define DB double
#define U unsigned
#define P std::pair
#define LL long long
#define LD long double
#define pb push_back
#define MP std::make_pair
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 100+5;
const int ha = 998244353;
std::string str;
inline void add(int &x,int y){
x += y-ha;x += x>>31&ha;
}
std::map<std::string,int> f,g;
int F(std::string);
int G(std::string);
inline int F(std::string s){
if(s.empty()) return 1;
if(f.count(s)) return f[s];
int res = 0;
FOR(i,1,SZ(s)) add(res,1ll*G(s.substr(0,i))*F(s.substr(i,SZ(s)-i))%ha);
f[s] = res;return res;
}
inline int G(std::string s){
if(s.empty()) return 1;
if(s == "0") return 1;
if(s == "1") return 2;
if(g.count(s)) return g[s];
int res = 0;
FOR(d,1,SZ(s)-1){
if(SZ(s)%d) continue;
std::string nxt="";
FOR(i,0,d-1){
bool flag = 1;
for(int j = i;j < SZ(s);j += d) flag &= (s[j]=='1');
if(flag) nxt += "1";
else nxt += "0";
}
add(res,F(nxt));
}
return res;
}
int main(){
std::cin >> str;
printf("%d\n",F(str));
return 0;
}