zoukankan      html  css  js  c++  java
  • 组合数计数+卡常优化+dp——cf895D

    大致思路是数位dp里当前位limit的那一套思想,从前往后递推计算,卡常数比较恶心

    /* 
    i:1->n
    枚举第i位[a,z] 
    第i位<s[i]:那么后面就是个全排列,维护一个当前已经用掉的字符桶use,算出贡献直接加到答案里
        算贡献:(n-i)!/(cnta-usea)!...(cntz-usez)!
        如果暴力求复杂度得1e6*26*26
            所以我们预处理好一个tmp=(n-i)!/(cnta-usea)!...(cntz-usez)!
            当第i位选择ch时,将(cnt[ch]-use[ch])!这个底数换成(cnt[ch]-use[ch]-1)!即可 
                
    第i为==s[i]:后面的排列个数需要通过i+1位来确定,所以将第i位确定为s[i],更新use即可
     
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define mod 1000000007
    #define N 1000005
    
    char a[N],b[N];
    ll n,cnt[N],use[N],F[N],invf[N],inv[N];
    inline ll mul(ll a,ll b){
        return a*b%mod;
    }
    
    ll solve(char *s){
        ll res=0;
        memset(use,0,sizeof use);
        
        for(int i=1;i<=n;i++){//前i-1位都和s相同,第i位遍历[a,z] 
            int equal=0;
            ll tmp=F[n-i],tmpp;
            for(int k=0;k<26;k++)//先预处理一个值,防止后面累乘 
                if(cnt[k]>use[k])
                    tmp=mul(tmp,invf[cnt[k]-use[k]]);
            tmpp=tmp;
            
            for(int j=0;j<26;j++){//第i位放j 
                if(cnt[j]-use[j]==0)continue;//已经用完了
                if(j<s[i]-'a'){
                    tmp=mul(tmp,F[cnt[j]-use[j]]);//消去原来j底数的影响 
                    tmp=mul(tmp,invf[cnt[j]-use[j]-1]);//增加j-1作为底数 
                    res=(res+tmp)%mod;
                    tmp=tmpp;//恢复到原来的tmp 
                } 
                else if(j==s[i]-'a'){ 
                    use[j]++;
                    equal=1;break;
                }
            }
            if(!equal)break;
        }
        
        return res;
    }
    
    int main(){
        scanf("%s%s",a+1,b+1);
        //for(int i=1;i<=1000000;i++)a[i]='z',b[i]='z'; 
        n=strlen(a+1);
        /*if(n>=10000){
            cout<<n;return 0;
        }*/
        F[0]=1;invf[0]=1;inv[1]=1;
        for(int i=2;i<=1000000;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        for(int i=1;i<=1000000;i++)F[i]=F[i-1]*i%mod;
        for(int i=1;i<=1000000;i++)invf[i]=invf[i-1]*inv[i]%mod;
        
        for(int i=1;i<=n;i++)cnt[a[i]-'a']++;
            
        cout<<(solve(b)-solve(a)-1+mod)%mod<<'
    ';
    }
  • 相关阅读:
    PreparedStatement/Statement处理insert update等操作时乱码,以及URL
    MySQL unique 注意
    web乱码解决了
    实用工具及Chrome插件及实用网站(持续更新中...)
    前端常用算法集合,持续更新...
    各种问题汇总解决方法,持续更新中...
    node实现自动化图片切割压缩, 部署页面
    移动端图片缩放插件Pinchzoom.js
    提示框简单封装
    模拟滚动条
  • 原文地址:https://www.cnblogs.com/zsben991126/p/12295394.html
Copyright © 2011-2022 走看看