牛客【2021寒假集训营第一场】A-串

【2021寒假集训营第一场】A-串

dp,容斥原理,快速幂

传送门

这个题目就是容斥原理的应用,关键是弄明白长度为i的字符串根据us分为几种

长度为i的字符串主要分为三种:

  • 不含u的
  • 含u不含us的
  • 含us的

这三种加起来就是全集

解法一:

使用\(f[i]\)表示长度为i的含有us的字符串

可以分为以下两种:

  • 前i-1个字符串含有us,那么第i个字符可以是26个字母中任意一个:\(f[i] += f[i-1]*26\)
  • 前i-1个字符含有u,但是不含有us,那么第i个字符只需要填一个i即可:\(f[i] += 26^{i-1} -25^{i-1} - f[i-1]\)
    • 前i-1个字符含有u(即全部组合减去不含有u的组合):\(26^{i-1} -25^{i-1}\)
    • 不含有us,上面的结果减去含有us的即可,\(26^{i-1} -25^{i-1}-f[i-1]\)

所以结果就是\(f[i] = 26^{i-1} -25^{i-1} + 25* f[i-1]\)

注意这里计算幂的时候要使用到快速幂算法,其中还需要取模运算

ac代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
int n;
ll f[2000010];
int q_pow(ll a, ll n)
{
	ll ans = 1;
    ll tot = a; // 这里必须是ll类型,不然就会出错!因为tot是指数上升的 
	while(n){
		if(n&1) ans = (ans*tot)%mod;
		tot = (tot*tot)%mod;
		n>>=1;
	}
	return ans%mod;
}

int main()
{
	cin >> n;
	f[2] = 1;
	for(int i=3; i<=n; ++i){
		f[i] = (q_pow(26, i-1) - q_pow(25, i-1) + f[i-1]*25 % mod + mod)%mod; // 括号里面+mod是为了防止负数 
	}
	ll ans = 0;
	for(int i=2; i<=n; ++i){
		ans = (ans+f[i])%mod;
	}
	printf("%lld\n", ans);
	return 0;
}

解法二:

将一个长为i的字符串分为三类,并用\(f[i][0/1/2]\)表示

  • \(f[i][0]\)表示的是长度为i,不含有u的字符串
  • \(f[i][1]\)表示的是长度为i,含有u,不含有us的字符串
  • \(f[i][2]\)表示的是含有us的字符串

则可以得到以下关系:

  1. \(f[i][0] = 25*f[i-1][0]\);即前i个不含有u的字符串为前i-1个不含u的字符串,末尾添加一个非u字符
  2. \(f[i][1] = f[i-1][0] + f[i-1][1]*25\);如1,根据公式理解即可
  3. \(f[i][2] = f[i-1][1] + f[i-1][2]*26\);如1,根据公式理解即可

ac 代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
int n;
ll f[1000010][3];
int main()
{
	cin >> n;
	f[2][0] = 25*25; 
	f[2][1] = 50; 
	f[2][2] = 1;
	for(int i=3; i<=n; ++i){
		f[i][0] = f[i-1][0]*25%mod;
		f[i][1] = (f[i-1][0] + 25*f[i-1][1])%mod;
		f[i][2] = (f[i-1][1] + 26*f[i-1][2])%mod;
	} 
	ll ans = 0;
	for(int i=2; i<=n; ++i){
		ans = (ans+f[i][2])%mod;
	}
	cout << ans;
	return 0;
}
posted @ 2021-02-16 16:16  VanHope  阅读(51)  评论(0编辑  收藏  举报