jzoj 4812. 【NOIP2016提高A组五校联考2】string 排列组合+乘法逆元

来源:互联网 发布:js包装函数是什么 编辑:程序博客网 时间:2024/05/01 03:31

Description

        给出一个长度为n, 由小写英文字母组成的字符串S, 求在所有由小写英文字母组成且长度为n 且恰好有k 位与S 不同的字符串中,给定字符串T 按照字典序排在第几位。
         由于答案可能很大,模10^9 + 7 输出。

Input

第一行为两个整数n; k
第二行一个字符串S
第三行一个字符串T,(T即是k位与S不同的串)

Output

输出一行取模后的答案。

Sample Input

4 1abcdbbcd

Sample Output

76

Data Constraint

对于前30% 的数据,n<=5
对于100% 的数据,k<=n<=10^5

分析:这题被我当场AC了,好开森~~

实际上就是统计有多少个长度为n的字符串满足和S恰好有k个位置不同且比T要小。

那么我们就从第一位开始统计:

首先字符串的第一位肯定不能大于T的第一位

那么如果S的第一位比T的第一位要小,那么第一位对答案的贡献就是

C(n-1,m-1)*25^(m-1)*(T[1]-'a'-1)+C(n-1,m)*25^m然后要m--

如果S的第一位比T的第一位要大,那么第一位对答案的贡献就是C(n-1,m-1)*25^(m-1)*(T[1]-'a')然后要m--

如果S的第一位等于T的第一位,那么对答案的贡献就是C(n-1,m-1)*25^(m-1)*(T[1]-'a')

然后往下一位一样的接着处理就好了。


代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define N 100005#define ll long long#define MOD 1000000007using namespace std;int mi[N],ny[N],jc[N];char s1[N],s2[N];int ksm(int x,int y){if (!y) return 1;if (y==1) return x;int w=ksm(x,y/2);w=(ll)w*w%MOD;if (y%2==1) w=(ll)w*x%MOD;return w;}int main(){freopen("string.in","r",stdin);freopen("string.out","w",stdout);int n,m;scanf("%d%d",&n,&m);jc[0]=1;ny[0]=1;for (int i=1;i<=n;i++){jc[i]=(ll)jc[i-1]*i%MOD;ny[i]=ksm(jc[i],MOD-2);}mi[0]=1;for (int i=1;i<=m;i++)mi[i]=(ll)mi[i-1]*25%MOD;scanf("%s",s1);scanf("%s",s2);int ans=0;for (int i=0;i<n;i++){if (n-i<m) break;if (s1[i]<s2[i]){int x=(ll)jc[n-i-1]*ny[m]%MOD;x=(ll)x*ny[n-i-1-m]%MOD;x=(ll)x*mi[m]%MOD;ans=(ans+x)%MOD;x=(ll)jc[n-i-1]*ny[m-1]%MOD;x=(ll)x*ny[n-i-m]%MOD;x=(ll)x*mi[m-1]%MOD;x=(ll)x*(s2[i]-'a'-1)%MOD;ans=(ans+x)%MOD;m--;}else if (s1[i]==s2[i]){int x=(ll)jc[n-i-1]*ny[m-1]%MOD;x=(ll)x*ny[n-i-m]%MOD;x=(ll)x*mi[m-1]%MOD;x=(ll)x*(s2[i]-'a')%MOD;ans=(ans+x)%MOD;}else{int x=(ll)jc[n-i-1]*ny[m-1]%MOD;x=(ll)x*ny[n-i-m]%MOD;x=(ll)x*mi[m-1]%MOD;x=(ll)x*(s2[i]-'a')%MOD;ans=(ans+x)%MOD;m--;}}ans=(ans+1)%MOD;printf("%d",ans);return 0;}



0 0
原创粉丝点击