zoukankan      html  css  js  c++  java
  • Codeforces Round448 D

    Codeforces Round448 D

    题目大意

    有两个长度相同的字符串a,b(长度不超过1e6),构造一个字符串c,使得c是a的重排序,并且c的字典序大于a,c的字典序小于b,问共有多少种构造的方法,答案对1e9+7取模

    思路

    若只考虑c>a,那么从前往后遍历,如果当前取了一个(c_i>a_i) ,那么之后的所有排法都成立(用组合数求);如果当前(c_i=a_i) ,那么就再往下一个考虑。

    同理,若只考虑c<b是一样的。

    那么可以用两个标记igna和ignb来表示是否忽视a、b的存在,当(c_i=a_i) 时,并且igna为0时,就往下一个考虑,并根据(c_i) 是否小于(b_i) 来修改下一个的ignb值;(c_i=b_i) 则同理,反之则为所有排放都成立的情况。

    这种算法需要枚举每一位,在当前为枚举26个字母,在求组合数时也需要枚举26个字母的排法,时间复杂度是(O(n*k^2)),n=1e6,k=26,是超时的。

    优化:考虑到在求组合数时,不需要每次从头算一遍,而可以在一开始记录全排列的情况,每枚举一位,记录用掉当前的(c_i)后的组合数。

    假设字母的个数为cnt[i]。一开始的全排列为(C(n,cnt[1])*C(n-cnt[1],cnt[2])*C(n-cnt[1]-cnt[2],cnt[3])* cdots C(n-cnt[1]-cnt[2]-cdots -cnt[n-1],cnt[n])) ,其中,(n-cnt[1]-cnt[2]-cdots -cnt[n-1] = cnt[n]) ,化简后为(cfrac{(n-pos)!}{cnt[1]! imes cnt[2]! imes cnt[3]! imes cdots imes cnt[n]!}) ,而当前的则为(cfrac{(n-pos-1)!}{cnt[1]! imes cnt[2]! imes cnt[3]! imes cdots imes (cnt[i]-1)! imes cdots imes cnt[n]!}) ,其中i为当前选的字符,pos位选的位置,从0开始

    那么当前的就等于之前的( imes cfrac{(n-pos-1)!}{(n-pos)!} imes cfrac{cnt[i]!}{(cnt[i]-1)!}​) ,即可。

    代码

    //http://codeforces.com/contest/895/problem/D
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    #include<ctime>
    #include<queue>
    #include<vector>
    #include<map>
    #define MAXN 1000005
    const int inf=0x3f3f3f3f;
    #define rep(i,n) for(i=0;i<n;i++)
    #define mem(a,x) memset(a,x,sizeof(a))
    const double PI=acos(-1);
    const double eps=1e-9;
    using namespace std;
    typedef long long ll;
    const ll Mod = 1e9+7;
    int t,i,j,k,n,m,num[300];
    ll ans;
    char a[MAXN],b[MAXN],c[MAXN]; 
    int fac[MAXN],inv[MAXN];
    int fp(int a,int k)
    {
        int res=1;
        while(k)
        {
            if(k&1)res=1LL*res*a%Mod;
            a=1LL*a*a%Mod;
            k>>=1;
        }
        return res;
    }
    void build()
    {
        for(int i=(fac[0]=1);i<(MAXN);i++)
            fac[i]=1LL*i*fac[i-1]%Mod;
        inv[MAXN-1]=fp(fac[MAXN-1],Mod-2);
        for(int i=MAXN-2;i>=0;i--)
            inv[i]=1LL*(i+1)*inv[i+1]%Mod;
        
        
    }
    int C(int n,int k)
    {
        return 1LL*fac[n]*inv[k]%Mod*inv[n-k]%Mod;
    }
    ll cal(int x){
    	ll cnt=1;
    	int d=n-x-1;
    	for(char i='a';i<='z';i++){
    		if(num[i]){
    			cnt*=C(d,num[i]);
    			cnt%=Mod;
    			d-=num[i];
    		}
    	}
    	return cnt;
    }
    int dfs(int pos,int igna,int ignb,int cc){
    	if(pos==n){
    		return 0;
    	}
    	char l=a[pos],r=b[pos];
    	//if(igna||ignb){
    		if(igna) l='a';
    		if(ignb) r='z';
    	//}
    		for(char i=l;i<=r;i++){
    			if(!num[i]) continue;
    			int nxc=1LL*cc*fac[n-pos-1]%Mod*inv[n-pos]%Mod*fac[num[i]]%Mod*inv[num[i]-1]%Mod;
    			if(i==a[pos]&&(!igna)){
    				num[i]--;
    				dfs(pos+1,0,ignb||(i<b[pos]),nxc);
    				num[i]++;
    			}
    			else if(i==b[pos]&&(!ignb)){
    				num[i]--;
    				dfs(pos+1,igna||(i>a[pos]),0,nxc);
    				num[i]++;
    			}
    			else{
    				num[i]--;
    				ans+=nxc;
    				ans%=Mod;
    				num[i]++;
    			}
    		}
    	
    	
    }
    int main(){
    	build();
    	scanf("%s%s",a,b);
    	n=strlen(a);
    	ans=0;
    	for(int i=0;i<n;i++){
    		num[a[i]]++;
    	}
    	int cc=fac[n];
    	for(int i='a';i<='z';i++){
    		if(num[i]){
    			cc=1LL*cc*inv[num[i]]%Mod;
    		}
    	}
    	dfs(0,0,0,cc);
    	printf("%lld
    ",ans);
    	return 0;
    }
    /*
    abacaba
    ubuduba
    
    abazaba
    zbzzzba
    
    */
    
  • 相关阅读:
    UML图箭头关系
    使用 Python 编写 vim 插件
    linux grep命令
    gevent For the Working Python Developer
    坐标系旋转变换公式图解
    欲哭无泪的p-value = 0.051 | 做几次重复能得到较低的p-value
    RNA-seq要做几次生物学重复?找出来的100%都是真正的应答基因
    Strand Specific mRNA sequencing 之重要性与分析
    为什么二代测序的原始数据中会出现Read重复现象?
    DNA甲基化研究概述
  • 原文地址:https://www.cnblogs.com/Crazycatmiao/p/7932210.html
Copyright © 2011-2022 走看看