传送门
解题思路
首先可以发现,无论怎么变,字母的和是不变的。
而且每个位置是多少都行(只要和别超)。
用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(Tlog T+26 imes 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;
}