【题解】「abc171F」Strivore

题意

求出在一个字符串 s s s 中插入 n n n 个小写字母,有多少不同的结果。 n ≤ 1 0 6 n\leq 10^6 n106

solution:

神仙计数题。本题可以等价转化为,有多少长度为 n + m n+m n+m 的字符串存在子序列串 s s s 。为了避免算重,我们规定如果有相同字符,则取序列中的最后一个 。

例如:字符串 abbcddc 的子序列 bc,从字符串的结尾往开头找,c 找到字符串末尾,b 找到字符串第三位。不难发现,对于一个给定的字符串所对应的唯一子序列是确定的。

那么我么就可以考虑枚举开头位置 1 ≤ i ≤ m + 1 1\leq i \leq m+1 1im+1 i i i 前面的每一个数都没有限制,总共 2 6 i − 1 26^{i-1} 26i1 种可能。对于后面 n + m − i n+m-i n+mi 个数,首先选出 m − 1 m-1 m1 个数确定子序列在字符串中的位置;对于剩下 n − i + 1 n-i+1 ni+1 个不在子序列中的数,为了满足定义,我们知道不能和它后继的子序列中的字符一样,所以总共 2 5 n − i + 1 25^{n-i+1} 25ni+1 种可能。

如图,可以发现的确有 25 25 25 种可能。

在这里插入图片描述

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

其实这道题我最开始的想法是考虑 26 26 26 种颜色,也能保证字符串长得不一样,但是似乎很难算答案。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mx=2e6+5;
const int mod=1e9+7;
int n,m;
ll res,fac[mx],inv[mx];
//相同的字符 x ,取序列中的最后一个 
ll fpow(ll x,ll y) {
	ll mul=1;
	for(;y;y>>=1) {
		if(y&1) mul=mul*x%mod;
		x=x*x%mod;
	}
	return mul; 
} 
ll C(ll x,ll y) {
	return fac[x]*inv[x-y]%mod*inv[y]%mod;
}
ll A(ll x,ll y) {
	return fac[x]*inv[y]%mod;
}
ll G(ll x,ll y) {
	return C(x+y-1,y-1);
}
char s[mx];
int main() {
//	freopen("data.in","r",stdin);
	scanf("%d%s",&m,s+1),n=strlen(s+1);
    fac[0]=1;
	for(int i=1;i<=2000000;i++) fac[i]=fac[i-1]*i%mod;
	inv[2000000]=fpow(fac[2000000],mod-2);
	for(int i=2000000;i>=1;i--) {
		inv[i-1]=inv[i]*i%mod;
	}
	for(int i=1;i<=m+1;i++) {
		ll turn1=fpow(26,i-1),turn2=C(n+m-i,n-1)*fpow(25,n+m-i-(n-1))%mod;
		res=(res+turn1*turn2%mod)%mod;
	}
	cout<<res;
}
posted @ 2021-07-09 16:06  仰望星空的蚂蚁  阅读(16)  评论(0)    收藏  举报  来源