01二重退背包+组合数学——cf1111d

退背包进阶,还是挺难想的

/* 
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<<'
';
    }
}
原文地址:https://www.cnblogs.com/zsben991126/p/11157664.html