• 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<<'
    ';
        }
    }
  • 相关阅读:
    在次转发IBATIS模糊查询
    IBatisNet 模糊查询
    转js动态给table添加行(tr)
    简单的ajax(菜鸟级)
    Redis消息通知系统的实现 新风宇宙
    彻底删除SVN版本库某一文件夹或文件 新风宇宙
    代码审查:ThoughtBot官方给出的代码审查指导原则 新风宇宙
    linux下php代码加密扩展beast 新风宇宙
    利用curl的API进行开发 新风宇宙
    Linux 日志服务器搭建(rsyslog+loganalyzer) 新风宇宙
  • 原文地址:https://www.cnblogs.com/zsben991126/p/11157664.html
Copyright © 2020-2023  润新知