【线段树维护状态转移矩阵】CodeForces - 719E Sasha and Array NOWCODER D 整数序列

本文介绍了一种通过矩阵运算优化区间操作与查询的技术方案。针对斐波那契数列及三角函数序列,利用矩阵乘法和快速幂实现区间加法,并高效地计算区间内元素的总和。

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

Step1 Problem:

给你 n 个数代表斐波那契数列下标, m 次操作。
操作分为两种类型;
1:区间 L 到 R 加 v。
2:求区间 L 到 R 的下标对应的斐波那契数列的和。
数据范围:
1 <= n <= 100000, 1 <= m <= 100000, 1 <= a[i] <= 1e9.

Step2 Ideas:

这种类型的题,区间加 v 一般可以提取出来。
F(i):斐波那契数列第 i 项。
F(i)        = [1, 1] * f(i-1)
F(i-1)     = [1, 0] * f(i-2)
例如:
F(a1) + F(a2) + F(a3) + F(a4) 对其下标进行区间加 v
F(a1+v) + F(a2+v) + F(a3+v) + F(a4+v) = ( F(a1) + F(a2) + F(a3) + F(a4) ) * 矩阵的 v 次方。
这时候我们只要维护矩阵即可
注意:矩阵A * 矩阵B 不一定等于 矩阵B * 矩阵A,所以谁乘谁很重要。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lson root<<1
#define rson root<<1|1
#define MID int mid = (l+r)/2
const int N = 1e5+5;
const int MOD = 1e9+7;
struct node
{
    ll a[2][2];
};
node c, ans, e;
node data[N<<2], lazy[N<<2];
ll w[N];
node mul(node x, node y)//矩阵相乘
{
    node sum;
    memset(sum.a, 0, sizeof(sum.a));
    for(int k = 0; k < 2; k++)
    {
        for(int i = 0; i < 2; i++)
        {
            if(x.a[i][k])
            for(int j = 0; j < 2; j++)
            {
                sum.a[i][j] += (x.a[i][k]*y.a[k][j])%MOD;
                sum.a[i][j] %= MOD;
            }
        }
    }
    return sum;
}
node Pow(node x, ll m)//快速幂
{
    node sum;
    memset(sum.a, 0, sizeof(sum.a));
    for(int i = 0; i < 2; i++) sum.a[i][i] = 1;
    while(m)
    {
        if(m&1) sum = mul(sum, x);
        x = mul(x, x);
        m >>= 1;
    }
    return sum;
}
node add(node x, node y)//矩阵相加
{
    node sum;
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
            sum.a[i][j] = (x.a[i][j] + y.a[i][j])%MOD;
    }
    return sum;
}
void creat(int root, int l, int r)
{
    lazy[root] = e;
    if(l == r) {
        scanf("%lld", &w[l]);
        data[root] = Pow(c, w[l]-1);//每个斐波那契值都用矩阵表示
        return ;
    }
    MID;
    creat(lson, l, mid);
    creat(rson, mid+1, r);
    data[root] = add(data[lson], data[rson]);
}
void pushdown(int root)
{
    if(lazy[root].a[0][0] == 1 && lazy[root].a[1][1] == 1 && lazy[root].a[0][1] == 0 && lazy[root].a[1][0] == 0) return ;
    data[lson] = mul(lazy[root], data[lson]);
    data[rson] = mul(lazy[root], data[rson]);
    lazy[lson] = mul(lazy[root], lazy[lson]);
    lazy[rson] = mul(lazy[root], lazy[rson]);
    lazy[root] = e;
}
void updata(int root, int l, int r, int ul, int ur, node v)
{
    if(ul <= l && r <= ur)
    {
        lazy[root] = mul(v, lazy[root]);
        data[root] = mul(v, data[root]);
        return ;
    }
    pushdown(root);
    MID;
    if(ul <= mid) updata(lson, l, mid, ul, ur, v);
    if(ur > mid) updata(rson, mid+1, r, ul, ur, v);
    data[root] = add(data[lson], data[rson]);
}
ll query(int root, int l, int r, int ul, int ur)
{
    if(ul <= l && r <= ur)
    {
        return data[root].a[0][0];
    }
    pushdown(root);
    MID;
    ll ret = 0;
    if(ul <= mid) ret += query(lson, l, mid, ul, ur), ret%=MOD;
    if(ur > mid) ret += query(rson, mid+1, r, ul, ur), ret%=MOD;
    return ret;
}
void init()
{
    c.a[0][0] = c.a[0][1] = 1;
    c.a[1][0] = 1; c.a[1][1] = 0;
    memset(ans.a, 0, sizeof(ans.a));
    ans.a[0][0] = 1;
    e.a[0][0] = 1; e.a[1][1] = 1;
    e.a[0][1] = 0; e.a[1][0] = 0;
}
int main()
{
    int n, m;
    while(~scanf("%d %d", &n, &m))
    {
        init();
        creat(1, 1, n);
        int ul, ur, ok;
        ll v;
        while(m--)
        {
            scanf("%d", &ok);
            if(ok == 1)
            {
                scanf("%d %d %lld", &ul, &ur, &v);
                node tmp = Pow(c, v);
                updata(1, 1, n, ul, ur, tmp);
            }
            else
            {
                scanf("%d %d", &ul, &ur);
                printf("%lld\n", query(1, 1, n, ul, ur));
            }
        }
    }
    return 0;
}

Step1 Problem:

链接:https://www.nowcoder.com/acm/contest/160/D
来源:牛客网

给出一个长度为n的整数序列a1,a2,…,an,进行m次操作,操作分为两类。
操作1:给出l,r,v,将al,al+1,…,ar分别加上v;
操作2:给出l,r,询问 这里写图片描述
数据范围:
1 <= n, m, a[i], v <= 200000; 1 <= l <= r <= n, v 是整数

Step2 Ideas:

前置技能:sin(a+v) = sin(a)cos(v) + cos(a)sin(v), cos(a+v) = cos(a)cos(v) - sin(a)sin(v);
sin(a1+v) + sin(a2+v) + sin(a3+v) = ( sin(a1)+sin(a2)+sin(a3) ) * cos(v) + ( cos(a1) + cos(a2) + cos(a3) ) * sin(v);
cos(a1+v) + cos(a2 + v) + cos(a3 + v) = ( sin(a1)+sin(a2)+sin(a3) ) * (-sin(v)) + ( cos(a1) + cos(a2) + cos(a3) ) * cos(v);
sin(a+v)        = [cos(v), sin(v) ]  * sin(a)
cos(a+v)       = [-sin(v), cos(v)] * cos(a)
区间加 可以变成 区间乘矩阵

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lson root<<1
#define rson root<<1|1
#define MID int mid = (l+r)/2
const int N = 2e5+5;
struct node
{
    double a[2][2];
};
node c, ans, e;
node data[N<<2], lazy[N<<2];
node mul(node x, node y)
{
    node sum;
    memset(sum.a, 0, sizeof(sum.a));
    for(int k = 0; k < 2; k++)
    {
        for(int i = 0; i < 2; i++)
        {
            if(x.a[i][k])
            for(int j = 0; j < 2; j++)
            {
                sum.a[i][j] += x.a[i][k]*y.a[k][j];
            }
        }
    }
    return sum;
}
node Pow(node x, ll m)
{
    node sum;
    memset(sum.a, 0, sizeof(sum.a));
    for(int i = 0; i < 2; i++) sum.a[i][i] = 1;
    while(m)
    {
        if(m&1) sum = mul(sum, x);
        x = mul(x, x);
        m >>= 1;
    }
    return sum;
}
node add(node x, node y)
{
    node sum;
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
            sum.a[i][j] = x.a[i][j] + y.a[i][j];
    }
    return sum;
}
void creat(int root, int l, int r)
{
    lazy[root] = e;
    if(l == r) {
        int num;
        scanf("%d", &num);
        memset(data[root].a, 0, sizeof(data[root].a));
        data[root].a[0][0] = sin(num);
        data[root].a[1][0] = cos(num);
        return ;
    }
    MID;
    creat(lson, l, mid);
    creat(rson, mid+1, r);
    data[root] = add(data[lson], data[rson]);
}
void pushdown(int root)
{
    if(lazy[root].a[0][0] == 1 && lazy[root].a[1][1] == 1 && lazy[root].a[0][1] == 0 && lazy[root].a[1][0] == 0) return ;
    data[lson] = mul(lazy[root], data[lson]);
    data[rson] = mul(lazy[root], data[rson]);
    lazy[lson] = mul(lazy[root], lazy[lson]);
    lazy[rson] = mul(lazy[root], lazy[rson]);
    lazy[root] = e;
}
void updata(int root, int l, int r, int ul, int ur, node v)
{
    if(ul <= l && r <= ur)
    {
        lazy[root] = mul(v, lazy[root]);
        data[root] = mul(v, data[root]);
        return ;
    }
    pushdown(root);
    MID;
    if(ul <= mid) updata(lson, l, mid, ul, ur, v);
    if(ur > mid) updata(rson, mid+1, r, ul, ur, v);
    data[root] = add(data[lson], data[rson]);
}
double query(int root, int l, int r, int ul, int ur)
{
    if(ul <= l && r <= ur)
    {
        return data[root].a[0][0];
    }
    pushdown(root);
    MID;
    double ret = 0;
    if(ul <= mid) ret += query(lson, l, mid, ul, ur);
    if(ur > mid) ret += query(rson, mid+1, r, ul, ur);
    return ret;
}
void init()
{


    e.a[0][0] = 1; e.a[1][1] = 1;
    e.a[0][1] = 0; e.a[1][0] = 0;
}
int main()
{
    int n, m;
    while(~scanf("%d", &n))
    {
        init();
        creat(1, 1, n);
        scanf("%d", &m);
        int ul, ur, ok;
        int v;
        while(m--)
        {
            scanf("%d", &ok);
            if(ok == 1)
            {
                scanf("%d %d %d", &ul, &ur, &v);
                node tmp;
                tmp.a[0][0] = cos(v), tmp.a[0][1] = sin(v);
                tmp.a[1][0] = -sin(v), tmp.a[1][1] = cos(v);
                updata(1, 1, n, ul, ur, tmp);
            }
            else
            {
                scanf("%d %d", &ul, &ur);
                printf("%.1lf\n", query(1, 1, n, ul, ur));
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值