ACM学习历程—HDU 5459 Jesus Is Here(递推)(2015沈阳网赛1010题)

 Sample Input

9

5

6

7

8

113

1205

199312

199401

201314

 

 

Sample Output

Case #1: 5

Case #2: 16

Case #3: 88

Case #4: 352

Case #5: 318505405

Case #6: 391786781

Case #7: 133875314

Case #8: 83347132

Case #9: 16520782

题目要求当前字符串序列中某项里cff前缀两两间差值的和。

假设已经纪录了cff前缀的位置,

当前第k个字符串的位置为

v[1], v[2], …v[cnt(k)]

其中cnt(k)表示当前字符串cff前缀的个数。

那么对于k+1

u[1], u[2], …u[cnt(k+1)]

然后组合两个串

v[1], v[2], …v[cnt(k)], u[1]+len(k), u[2]+len(k),…, u[cnt(k+1)]+len(k)

其中len(k)表示第k个字符串的长度。

考虑前半部分v的内部差的和为sum(k), 后半部分u的内部和为sum(k+1)

然后就差交叉部分。

考虑v[i],

自然是u[1]+len(k)-v[i] + u[2]+len(k)-v[i] +…+ u[cnt(k+1)]+len(k)-v[i]

化简得s[k+1]+cnt(k+1)*len(k)-cnt(k+1)*v[i]

然后对i求和

cnt(k)*s[k+1]+cnt(k)*cnt(k+1)*len(k)-cnt(k+1)*s[k]

于是sum就是上面三部分的和。

然后只需要同时维护s, cnt, len即可。

s[k+2]=s[k]+s[k+1]+cnt(k+1)*len(k)

cnt(k+2)=cnt(k+1)+cnt(k)

len(k+2)=len(k+1)+len(k)

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <string>
#define LL long long
#define MOD 530600414

using namespace std;

const int maxN = 201400;
int n;
LL cnt[maxN], s[maxN], len[maxN], sum[maxN];

void init()
{
    cnt[3] = cnt[4] = 1;
    s[3] = 1; s[4] = 3;
    len[3] = 3; len[4] = 5;
    sum[3] = sum[4] = 0;
    for (int i = 5; i < maxN; ++i)
    {
        cnt[i] = (cnt[i-1]+cnt[i-2])%MOD;
        s[i] = (s[i-1]+s[i-2]+cnt[i-1]*len[i-2]%MOD)%MOD;
        len[i] = (len[i-1]+len[i-2])%MOD;
        sum[i] = (sum[i-1]+sum[i-2])%MOD;
        sum[i] += (cnt[i-2]*s[i-1]%MOD+(cnt[i-2]*cnt[i-1]%MOD)*len[i-2]%MOD-cnt[i-1]*s[i-2]%MOD)%MOD;
        sum[i] = (sum[i]%MOD+MOD)%MOD;
    }
}

int main()
{
    //freopen("test.in", "r", stdin);
    init();
    int T;
    scanf("%d", &T);
    for (int times = 0; times < T; ++times)
    {
        scanf("%d", &n);
        printf("Case #%d: %d
", times+1, sum[n]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/andyqsmart/p/4822178.html