ZOJ 3772 Calculate the Function(线段树+矩阵

本文介绍了一种利用线段树维护矩阵乘法的方法,以解决区间递推公式计算的问题,通过优化递推公式的计算过程,将复杂度降低至O(nlogn),并详细展示了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

求一个区间内的的递推公式的值 递推的区间为[l,r][l,r][l,r], 这样因为递推式和矩阵的性质是相同的, 我们可以考虑使用线段树维护矩阵的相乘 这样复杂度就可以做到O(nlogn)O(nlogn)O(nlogn)]

特别需要注意的事情是 因为这个原因wa了无数次 我们在线段树中的成分 一定要遵循矩阵相乘的顺序,因为矩阵我们维护的相乘顺序是从rrr −>->> lll 的 查询的时候当然也需要这样搞了

#include <bits/stdc++.h>
using namespace std;

#define cpp_io() {ios::sync_with_stdio(false); cin.tie(NULL);}
#define rep(i,a,n) for (int i=a;i<n;i++)
#define repp(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define CLR(a,b) memset(a,(b),sizeof(a))
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ls o<<1
#define rs o<<1|1


typedef long long ll;
typedef vector<int> VI;
const int MAXN = (int)1e5+10;
const int INF = 0x3f3f3f3f;
const int mod = (int)1e9+7;

void F() {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
}
struct mat {
    ll a[2][2];
    mat() {
        CLR(a, 0);
    }
    mat(ll x) {
        a[0][0] = a[1][0] = 1;
        a[1][1]=0; a[0][1] = x;
    }
    mat operator * (const mat &r)const {
        mat res;
        rep(i,0,2){
            rep(j,0,2){
                rep(k,0,2){
                    res.a[i][j]=(res.a[i][j]+a[i][k]*r.a[k][j])%mod;
                }
            }
        }
        return res;
    }
};

struct  node {
    int l,r;
    mat x;
}t[MAXN<<2];
ll arr[MAXN<<2];

void build(int l,int r,int o) {
    t[o].l=l,t[o].r=r;
    if(l==r) {
        t[o].x=mat(arr[l]);
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls); build(mid+1,r,rs);
    t[o].x=t[rs].x*t[ls].x;
}
mat query(int l,int r,int o) {
    if(t[o].l>=l&&t[o].r<=r) return t[o].x;
    int mid=(t[o].l+t[o].r)>>1;
    if(r<=mid) return query(l,r,ls);
    else if(l>mid) return query(l,r,rs);
    else {
        return query(mid+1,r,rs)*query(l,mid,ls);
    }
}
int main() {
    int T; cin>>T;
    while(T--) {
        int n,m; scanf("%d%d",&n,&m);
        repp(i,1,n) scanf("%lld",&arr[i]);
        build(1,n,1);
        while(m--) {
            int l,r; scanf("%d%d",&l,&r);
            if(r==l||r==l+1) {
                printf("%lld\n",arr[r]);
            }
            else {
                mat ans=query(l+2,r,1);
                printf("%lld\n",(ans.a[0][1]*arr[l]+ans.a[0][0]*arr[l+1])%mod);
            }
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值