ZOJ 3772 Calculate the Function 解题报告

来源:互联网 发布:丽江 大理 知乎 编辑:程序博客网 时间:2024/06/06 07:39

原题:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5235

题意:给定一个序列A,每次询问一组 L, R, 设 F(L) = AL, F(L+1) = AL+1, F(x) = F(x-1) + F(x-2) * Ax, 求F(R)


解法1:线段树

对于形如f(x) = p * f(x-1) + q * f(x-2)的通项都可以转成矩阵乘法,对于本题就是


于是依次拓展后得到



对于线段树的每个结点保存一个矩阵,如果结点是叶子,则有 a[0][0] = a[1][0] = 1, a[0][1] = Ax, a[1][1] = 0的形式

否则保存 a[r] * a[r-1] * ... * a[l] 的结果矩阵,要注意不要乘反了


于是对于每一个询问 l, r, 答案就是 find(l + 2, r) 的结果矩阵,再去乘 Al+1 Al 矩阵的结果


代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <string>#include <vector>#include <map>#include <cmath>using namespace std;#define LL long long#define ULL unsigned long long#define mod 1000000007#define eps 1e-8#define MP make_pair#define mxn 100005LL A[mxn];int ll[mxn << 2], rr[mxn << 2];struct Matrix {    LL a[2][2];    Matrix(){}    Matrix( int x ){        a[0][0] = a[1][0] = 1;        a[0][1] = x;        a[1][1] = 0;    }}M[mxn << 2];Matrix mul(Matrix A, Matrix B) {    Matrix C;    memset(C.a, 0, sizeof(C.a));    for( int i = 0; i < 2; ++i )        for( int j = 0; j < 2; ++j )            for( int k = 0; k < 2; ++k )                C.a[i][j] = (C.a[i][j] + A.a[i][k] * B.a[k][j]) % mod;    return C;}void build( int l, int r, int i ) {    ll[i] = l, rr[i] = r;    if( l == r ) {        M[i] = Matrix(A[l]);        return ;    }    int m = (ll[i] + rr[i]) >> 1, ls = i << 1, rs = ls | 1;    build(l, m, ls), build(m + 1, r, rs);    M[i] = mul(M[rs], M[ls]);}Matrix find( int l, int r, int i ) {    if( ll[i] == l && rr[i] == r ) return M[i];    int m = (ll[i] + rr[i]) >> 1, ls = i << 1, rs = ls | 1;    if( r <= m ) return find(l, r, ls);    if( l  > m ) return find(l, r, rs);    return mul(find(m + 1, r, rs), find(l, m, ls));}int main(){    //ios_base::sync_with_stdio(false);    int t, n, m, l, r;    cin >> t;    while( t-- ) {        cin >> n >> m;        for( int i = 1; i <= n; ++i ) scanf( "%lld", A + i );        build(1, n, 1);        while( m-- ) {            scanf( "%d%d", &l, &r );            if( r - l <= 1 ) printf( "%lld\n", A[r] );            else {                Matrix C = find(l + 2, r, 1);                LL ans = C.a[0][0] * A[l+1] % mod + C.a[0][1] * A[l] % mod;                printf( "%lld\n", ans % mod );            }        }    }    return 0;}


解法2:分块

比赛的时候智商结石了,可耻的分块了:D

假设序列从F1开始,由于F(x) = F(x-1) + F(x-2) * Ax ,可知 F1 和 F2 永远不会乘在同一个多项式内

故设 F(x) = B(x) + C(x), 其中B(x)为含有F1的多项式之和,C(x)为含有F2的多项式之和

于是我们把序列分成长为300的段,每段令F1 = F2 = 1,序列的第1个数做为F3,依次推到后面

那么对于一组确定的F1, F2, 我们有 F(x) = B(x) * F1 + C(x) * F2

当询问区间长度小于300时,可以直接撸,大于300时,先找到第一个大于l + 1的段,然后从Fl推到这个段的前面

由于我们把每段的头做为F3, 所以上一段的最后两个数,就是这一段的 F1, F2,代入即可


代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <string>#include <vector>#include <map>#include <cmath>using namespace std;#define LL long long#define ULL unsigned long long#define mod 1000000007#define eps 1e-8#define MP make_pair#define mxn 110005LL A[mxn], B[mxn], C[mxn], F[mxn];void init( int n ) {    for( int i = 1; i <= n; i += 300 ) {        B[i] = A[i]; C[i] = 1;        B[i+1] = A[i]; C[i+1] = A[i+1] + 1;        for( int j = i + 2; j < i + 300; ++j ) {            B[j] = (B[j-1] + B[j-2] * A[j]) % mod;            C[j] = (C[j-1] + C[j-2] * A[j]) % mod;        }    }}int main(){    //ios_base::sync_with_stdio(false);    int t, n, m, l, r;    cin >> t;    while( t-- ) {        cin >> n >> m;        for( int i = 1; i <= n; ++i ) scanf( "%lld", A + i );        init(n);        while( m-- ) {            scanf( "%d%d", &l, &r );            if( r - l <= 350 ) {                F[l] = A[l];                F[l+1] = A[l+1];                for( int i = l + 2; i <= r; ++i )                    F[i] = (F[i-1] + F[i-2] * A[i]) % mod;                printf( "%lld\n", F[r] );                continue;            }            int k = 1;            while( k <= l + 1 ) k += 300;            F[l] = A[l]; F[l+1] = A[l+1];            for( int i = l + 2; i < k; ++i )                F[i] = (F[i-1] + F[i-2] * A[i]) % mod;            for( int i = k, j; ; i = j ) {                j = i + 300;                if( j <= r ) {                    F[j-2] = (B[j-2] * F[i-2] % mod + C[j-2] * F[i-1] % mod) % mod;                    F[j-1] = (B[j-1] * F[i-2] % mod + C[j-1] * F[i-1] % mod) % mod;                }                else {                    F[r] = (B[r] * F[i-2] % mod + C[r] * F[i-1] % mod) % mod;                    printf( "%lld\n", F[r] );                    break;                }            }        }    }    return 0;}


0 0
原创粉丝点击