題解 Medium Counting
阿新 • • 發佈:2021-08-20
又是神仙DP
發現如果只有兩個串就很好做了
於是這個神仙DP定義就從這裡下手:令 $dp[p][c][l][r] 表示在 \([s_l, s_r]\) 這段字串中,考慮從第 \(p\) 個位置開始的字尾,並要求這個字元至少為 \(c\)
考慮轉移,因為這裡有個「至少」,第一個轉移是直接從 \(dp[p][c+1][l][r]\) 繼承過來
考慮第二個轉移
發現在 \([l, r]\) 中一定存在一個 \(c\) 到 \(c+1\) 的分界點,我們列舉這個分界點
同時如果我們強令為 \(c\) 的那些數與輸入衝突了就break掉
於是這部分的轉移方程為
這樣就(在一定程度上)轉化成了對兩個字串的處理
記得要把長度不夠的串用‘a'-1
Code:
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define N 100010 #define ll long long #define ull unsigned long long #define reg register int //#define int long long int n; char s[55][25]; int len[55], maxl; const ll mod=990804011ll, base=131; inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;} namespace force{ ull p[25]; inline ull hashing(ull* h, int l, int r) {return h[r]-h[l-1]*p[r-l+1];} ll dfs(int u, char* t, ull ht, ull hs, bool lim) { //cout<<"dfs "<<u<<' '<<ht<<' '<<hs<<' '<<lim<<endl; if (u>n) return 1; ull h[25]; h[0]=0; bool full=1; //cout<<"len: "<<len[u]<<endl; for (reg i=1; i<=len[u]; ++i) { //cout<<"i: "<<i<<endl; if (s[u][i]=='?') { full=0; ll ans=0; //cout<<"pos1"<<endl; for (reg j=(lim?t[i]-'a'+1:0); j<26; ++j) { s[u][i]='a'+j; h[i]=h[i-1]*base+s[u][i]; ans+=dfs(u, t, ht, h[i], lim&&(s[u][i]==t[i])); } s[u][i]='?'; return ans; } else { h[i]=h[i-1]*base+s[u][i]; if (lim && s[u][i]<t[i]) return 0; if (lim && s[u][i]!=t[i]) lim=0; } } if (full && !lim) return dfs(u+1, s[u], h[len[u]], 0, 1); return 0; } void solve() { printf("%lld\n", dfs(1, s[0], 0, 0, 0)); exit(0); } } namespace task1{ ull p[25]; struct st{int u; ull ht, hs; bool lim; st(int a, ull b, ull c, bool d):u(a),ht(b),hs(c),lim(d){}}; struct st_hash{inline size_t operator () (st a) const {return hash<ull>()(a.ht*a.hs);}}; inline bool operator == (st a, st b) {return a.u==b.u&&a.ht==b.ht&&a.hs==b.hs&&a.lim==b.lim;} unordered_map<st, ll, st_hash> mp{5000, st_hash()}; inline ull hashing(ull* h, int l, int r) {return h[r]-h[l-1]*p[r-l+1];} ll dfs(int u, char* t, ull ht, ull hs, bool lim) { //cout<<"dfs "<<u<<' '<<ht<<' '<<hs<<' '<<lim<<endl; if (u>n) return 1; st sit(u, ht, hs, lim); if (mp.find(sit)!=mp.end()) return mp[sit]; ull h[25]; h[0]=0; bool full=1; //cout<<"len: "<<len[u]<<endl; for (reg i=1; i<=len[u]; ++i) { //cout<<"i: "<<i<<endl; if (s[u][i]=='?') { full=0; ll ans=0; //cout<<"pos1"<<endl; for (reg j=(lim?t[i]-'a'+1:0); j<26; ++j) { s[u][i]='a'+j; h[i]=h[i-1]*base+s[u][i]; ans+=dfs(u, t, ht, h[i], lim&&(s[u][i]==t[i])), ans%=mod; } s[u][i]='?'; mp[sit]=ans; return ans; } else { h[i]=h[i-1]*base+s[u][i]; if (lim && s[u][i]<t[i]) {mp[sit]=0; return 0;} if (lim && s[u][i]!=t[i]) lim=0; } } if (full && !lim) { mp[sit]=dfs(u+1, s[u], h[len[u]], 0, 1); return mp[sit]; } return 0; } void solve() { printf("%lld\n", dfs(1, s[0], 0, 0, 0)%mod); exit(0); } } namespace task{ ll dp[25][30][52][52]; ll dfs(int p, int c, int l, int r) { //cout<<"dfs "<<p<<' '<<c<<' '<<l<<' '<<r<<endl; if (~dp[p][c][l][r]) return dp[p][c][l][r]; if (l>r) return dp[p][c][l][r]=1; if (p>maxl) return dp[p][c][l][r]=(l==r); if (c>26) return dp[p][c][l][r]=0; dp[p][c][l][r]=dfs(p, c+1, l, r); for (int i=l; i<=r; ++i) { //if (s[i][p]!=27&&s[i][p]!=c) break; if (!(s[i][p]==c || (s[i][p]==27&&c))) break; md(dp[p][c][l][r], dfs(p+1, 0, l, i)*dfs(p, c+1, i+1, r)%mod); } return dp[p][c][l][r]; } void solve() { memset(dp, -1, sizeof(dp)); printf("%lld\n", dfs(1, 0, 1, n)); exit(0); } } signed main() { scanf("%d", &n); for (reg i=1; i<=n; ++i) { scanf("%s", s[i]+1); len[i]=strlen(s[i]+1); maxl=max(maxl, len[i]); for (int j=1; j<=len[i]; ++j) if (s[i][j]=='?') s[i][j]=27; else s[i][j]-='`'; } //if (n*maxl<=10) force::solve(); //else task1::solve(); task::solve(); return 0; }