题意: 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;
}