【数论】贝壳找房计数比赛&&祭facinv

震惊!阶乘逆元处理背后竟有如此玄机……

题目描述

贝壳找房举办了一场计数比赛,比赛题目如下。

给一个字符串 s 和字符串 t,求出 s 的所有去重全排列中 t 出现的次数。比如aab的去重全排列为aabababaa。注意aaaa算出现两次aaa

你的老大希望你帮他写一个程序作弊。

输入格式

第一行一个整数 TT,表示数据组数。

每组数据中,第一行一个字符串 ss,第二行一个字符串 tt。

数据保证 1≤T≤100, 1≤∣t∣≤∣s∣≤105,t,s 只包含小写字母。

输出格式

输出一共 TT 行,每行一个整数,表示所求答案对 10^9+7取模的结果。

样例输入

2
aab
ab
aabb
ab

样例输出

2
6

题目分析

其实就是一道挺简单的数论基础题……

但是复赛时候我想复杂了很多,一直在考虑将目标串拆成多个原串后如何去重之类的问题。

例如原串=ab,目标串=ababaa。然后设t=ab,目标串就有taaab/ttaa这两种情况,于是陷入去重无法自拔……

呃实际上冷静分析就可以发现,只用考虑拆一次的结果,那么就套上可重全排列的公式就好了。

 #include<bits/stdc++.h>
const long long MO = 1e9+;
const long long maxn = ; long long ans,inv[maxn],mp[maxn];
int n,tt;
char s[maxn],t[maxn];
bool fl; int main()
{
inv[] = inv[] = ;
for (int i=; i<maxn; i++)
inv[i] = (long long)(MO-MO/i)*inv[MO%i]*inv[i-]%MO;
scanf("%d",&tt);
while (tt--)
{
fl = ;
memset(mp, , sizeof mp);
scanf("%s%s",s,t);
for (int i=; s[i]; i++)
mp[s[i]]++;
for (int i=; t[i]; i++)
mp[t[i]]--, fl = fl||(mp[t[i]]<);
if (fl){
printf("0\n");
continue;
}
n = strlen(s)-strlen(t);
ans = n+;
while (n--) ans = ans*(n+)%MO;
for (char i='a'; i<='z'; i++)
ans = (long long)ans*inv[mp[i]]%MO;
printf("%lld\n",ans);
}
return ;
}

然而!上面这个程序是会WA的!

在历经好长一段时间的调试之后,终于发现facinv中间溢出了……

那么大不了就改成这样嘛,反正是多一个%MO的事情

 inv[] = inv[] = ;
for (int i=; i<maxn; i++)
inv[i] = (MO-MO/i)%MO*inv[MO%i]%MO*inv[i-]%MO;

可是依旧WA   :)

 inv[] = inv[] = ;
for (int i=; i<maxn; i++)
inv[i] = (MO-MO/i)%MO*inv[MO%i]%MO;
for (int i=; i<maxn; i++) inv[i] = inv[i-]*inv[i]%MO;

最后只能改成上面这个样子……

行吧终于过了。

END

上一篇:2018.10.25 bzoj4517: [Sdoi2016]排列计数(组合数学)


下一篇:9月19日下午JavaScript数组冒泡排列和二分法