退背包进阶,还是挺难想的
/* dp1[k]表示取到体积k的方案数 dp2[i][j][k]表示左侧必选ij的情况下,取到体积k的方案数 dp2[i][j][k]=dp1[k]-左侧不选ij的方案数 但是这样比较难搞,我们把状态转换一下,dp2[i][j][k]表示左侧不选i,j,取到k的方案数 这样要两层退背包来解决 状态前两维可以直接压缩,用ans[i][j]来保存答案,复杂度O(52*52*n) */ #include<bits/stdc++.h> using namespace std; #define maxn 200005 #define ll long long #define mod 1000000007 ll f[maxn],inv[maxn],invf[maxn]; void init(){ f[0]=inv[1]=invf[1]=invf[0]=1; for(int i=2;i<maxn;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod; for(int i=1;i<maxn;i++){ f[i]=f[i-1]*i%mod; invf[i]=invf[i-1]*inv[i]%mod; } } char s[maxn]; ll cnt[100],n,C; ll dp1[maxn],dp2[maxn],dp3[maxn],ans[100][100]; void work(){ //先处理背包 dp1[0]=1; for(int i=0;i<52;i++) if(cnt[i]) for(int j=n/2;j>=cnt[i];j--) dp1[j]=(dp1[j]+dp1[j-cnt[i]])%mod; for(int i=0;i<52;i++) if(cnt[i]){ //先退去一层:没有底i个物品时的状态dp2 memset(dp2,0,sizeof dp2); for(int k=0;k<=n/2;k++) if(k<cnt[i]) dp2[k]=dp1[k]; else dp2[k]=(dp1[k]-dp2[k-cnt[i]]+mod)%mod; ans[i][i]=dp2[n/2]; for(int j=0;j<52;j++) if(j!=i && cnt[j]){ //再退一层背包 memset(dp3,0,sizeof dp3); for(int k=0;k<=n/2;k++) if(k<cnt[j])dp3[k]=dp2[k]; else dp3[k]=(dp2[k]-dp3[k-cnt[j]]+mod)%mod; ans[i][j]=ans[j][i]=dp3[n/2]; } } } int main(){ init(); scanf("%s",s); n=strlen(s); for(int i=0;i<n;i++){ if(s[i]<='Z' && s[i]>='A') cnt[s[i]-'A']++; else cnt[s[i]-'a'+26]++; } C=2*f[n/2]*f[n/2]%mod; for(int i=0;i<52;i++) C=C*invf[cnt[i]]%mod; work(); int q;cin>>q; while(q--){ char a,b;int c,d; scanf("%d %d",&c,&d); a=s[c-1],b=s[d-1]; if(a<='Z' && a>='A') c=a-'A'; else c=a-'a'+26; if(b<='Z' && b>='A') d=b-'A'; else d=b-'a'+26; cout<<ans[c][d]*C%mod<<'\n'; } }