洛谷 P1385 密令(dp,離線)
阿新 • • 發佈:2021-09-27
傳送門
解題思路
首先可以發現,無論怎麼變,字母的和是不變的。
而且每個位置是多少都行(只要和別超)。
用dp[i][j]表示前i位的和為j的方案數。
然後列舉第j位可能情況(0~25),從dp[i-1][j-k]轉移過來即可。
一種思路是先預處理這個dp陣列,然後回答詢問。
另一種思路是離線處理,這樣dp陣列的第一維可以滾動掉,逆序列舉j,邊求dp邊儲存答案。
再就是開long long可以使取模運算放到最後,這樣對於每個dp[i][j]只取模了一次。
我用的第二種思路,沒特意卡常(還用的關閉同步的cin、cout),交上去竟拿了個最優解。
第二種思路複雜度應該為 \(O(T\log T+26\times len_{max}sum_{max})\)
AC程式碼
#include<cstdio> #include<iostream> #include<cstring> #include<iomanip> #include<cmath> #include<algorithm> using namespace std; const int mod=1e9+7; int T; long long dp[3005],m; string s; struct node{ int id; long long len,sum,ans; }q[10005]; bool cmp1(node a,node b){ return a.len<b.len; } bool cmp2(node a,node b){ return a.id<b.id; } int main(){ ios::sync_with_stdio(false); cin>>T; for(int t=1;t<=T;t++){ cin>>s; q[t].id=t; q[t].len=s.length(); for(int i=0;i<q[t].len;i++){ q[t].sum+=s[i]-'a'; } m=max(m,q[t].sum); } sort(q+1,q+T+1,cmp1); int now=1; for(int i=0;i<=25;i++) dp[i]=1; while(now<=T&&q[now].len==1){ q[now].ans=1; now++; } for(int i=1;i<q[T].len;i++){ for(int j=m;j>=0;j--){ for(int k=1;k<=25;k++){ if(j<k) break; dp[j]+=dp[j-k]; } dp[j]%=mod; } while(now<=T&&q[now].len==i+1){ q[now].ans=dp[q[now].sum]; now++; } } sort(q+1,q+T+1,cmp2); for(int i=1;i<=T;i++){ cout<<q[i].ans-1<<endl; } return 0; }