HDU 5459 Jesus Is Here dp+预处理

题意: s1="c",s2="ff",对于任意i>=3,si=si-1+si-2。给定n,求n中所有满足i<j,sn[i..i+2]=sn[j..j+2]="cff"的(j - i) mod 530600414

T组数据,T<=100,每组包括一个n,n<=201314。

题解:这题有递推的感觉,但最后还是队友做出来的(给力的队友啊),先用3个数组,num[],len[],dis[]分别表示第i个串中c的个数(即等于cff的个数),这个串的长度,和所有的c到最后一个字符的距离之和。

然后num[i]=num[i-1]+num[i-2],len[i]=len[i-1]+len[i-2]。这都是显而易见的。至于dis,dis[i]=dis[i-1]+dis[i-2]+num[i-2]*len[i-1],也不难求得,相当于是前两个的和,但是前面的字符串每个c到最后一个字符的距离都多了len[i-1]。

有了这三个数组后,我们就可以推答案了,dp[i]=dp[i-1]+dp[i-2]+num[i-1]*dis[i-2]+num[i-2]*(num[i-1]*len[i-1]-dis[i-1])。为什么这样呢,因为我们要求两两个cff间的距离之和嘛,我们把两个串拼接起来了以后,每个串自己之间的两两ccf距离之和是不变的,所以把dp[i-1]和dp[i-2]加上就行,但是还多了一些左边串中的cff和右边串中的cff的距离之和,那么这个怎么求呢?我们可以表示成左边串的cff的到拼接位置的和*右边串的cff的个数(因为右边的每个cff都可以和左边每个cff计算一次啊)+左边串cff的个数*右边串的cff到拼接位置的和。那“右边串的cff到拼接位置的和”怎么求呢?我们有右边串的cff到最后一个字符的位置的和,两个和有什么关系呢?两个相加=len[]*num[],所以,我们可以用num[]*len[]-dis[]表示“右边串的cff到拼接位置的和”。

然后就得到了,因为我们有很T次查询,而且n不大,可以直接预处理保存下来。

然后就好啦


代码:

#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define mod 530600414
#define maxn 201400
#define ll long long
ll num[maxn], len[maxn], dis[maxn], dp[maxn];
void solve()
{
	num[1] = 1;
	num[2] = 0;
	len[1] = 1;
	len[2] = 2;
	for (int i = 3; i < maxn; ++i)
	{
		num[i] = (num[i - 1] + num[i - 2]) % mod;
		len[i] = (len[i - 1] + len[i - 2]) % mod;
	}
	dis[1] = dis[2] = 0;
	dis[3] = 2;
	for (int i = 4; i < maxn; ++i)
	{
		dis[i] = (dis[i - 1] + dis[i - 2] + num[i - 2] * len[i - 1] % mod) % mod;
	}
	dp[1] = dp[2] = dp[3] = dp[4] = 0;
	for (int i = 5; i < maxn; ++i)
	{
		dp[i] = (dp[i - 1] + dp[i - 2] + num[i - 1] * dis[i - 2] % mod + num[i - 2] * ((num[i - 1] * len[i - 1] - dis[i - 1]) % mod)) % mod;
	}
}
int main()
{
	//freopen("input.txt", "r", stdin);
	int T, n;
	solve();
	scanf("%d", &T);
	for (int kase = 1; kase <= T; ++kase)
	{
		scanf("%d", &n);
		printf("Case #%d: %I64d\n", kase, dp[n]);
	}
	//while (1);
	//system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值