CodeForces - 718C Sasha and Array

190 篇文章 2 订阅
19 篇文章 0 订阅

题面

题意

给出一串数,要求支持以下两个操作:
1.区间加
2.区间查询f(a[i])的和,f(i)表示斐波那契的第i项的值。

做法

对于求f(i)的值,肯定是利用矩阵快速幂,因此f(i)可以表示为(1 1 \n 1 0)^i,这样区间加p就可以转化为区间乘单位矩阵的p次方,而且矩阵乘法满足分配率,这样就可以用线段树来维护。
维护方法:
每个线段树的结点存两个矩阵(区间f(i)的和和懒惰标记),剩余操作与普通线段树基本相同。
注意:不要记录区间加的数值,因为这样每次下传标记都要进行一次矩阵快速幂,时间复杂度多了一个log。

代码

#include<iostream>
#include<cstdio>
#define ll long long
#define mid ((l+r)>>1)
#define N 100100
#define M 1000000007
using namespace std;

ll n,m,num[N],tt;
struct Jz
{
    ll num[2][2];
    Jz()
    {
        num[0][0]=num[0][1]=num[1][0]=num[1][1]=0;
    }
    init()
    {
        num[0][1]=num[1][0]=0;
        num[0][0]=num[1][1]=1;
    }
    Jz operator + (const Jz &u) const
    {
        ll i,j;
        Jz res;
        for(i=0;i<2;i++)
        {
            for(j=0;j<2;j++)
            {
                res.num[i][j]=num[i][j]+u.num[i][j];
                res.num[i][j]%=M;
            }
        }
        return res;
    }
    Jz operator * (const Jz &u) const
    {
        ll i,j,k;
        Jz res;
        for(i=0;i<2;i++)
        {
            for(j=0;j<2;j++)
            {
                for(k=0;k<2;k++)
                {
                    res.num[i][j]+=num[i][k]*u.num[k][j]%M;
                    res.num[i][j]%=M;
                }
            }
        }
        return res;
    }
    Jz po(ll u)
    {
        Jz res,tmp=*this;
        res.num[0][0]=res.num[1][1]=1;
        for(;u;)
        {
            if(u&1) res=res*tmp;
            tmp=tmp*tmp;
            u>>=1;
        }
        return res;
    }
    void out()
    {
        printf("%lld %lld\n%lld %lld\n",num[0][0],num[0][1],num[1][0],num[1][1]);
    }
};
Jz dw,tmp;
struct Node
{
    ll ls,rs;
    Jz jz,lazy;
};
Node node[N<<1];

void build(ll now,ll l,ll r)
{
    if(l==r)
    {
        node[now].jz=dw.po(num[l]);
        return;
    }
    if(l<=mid)
    {
        node[now].ls=++tt;
        build(tt,l,mid);
        node[now].jz=node[now].jz+node[node[now].ls].jz;
    }
    if(mid<r)
    {
        node[now].rs=++tt;
        build(tt,mid+1,r);
        node[now].jz=node[now].jz+node[node[now].rs].jz;
    }
}

inline void down(ll now)
{
    if(node[now].ls)
    {
        node[node[now].ls].lazy=node[node[now].ls].lazy*node[now].lazy;
        node[node[now].ls].jz=node[node[now].ls].jz*node[now].lazy;
    }
    if(node[now].ls)
    {
        node[node[now].rs].lazy=node[node[now].rs].lazy*node[now].lazy;
        node[node[now].rs].jz=node[node[now].rs].jz*node[now].lazy;
    }
    node[now].lazy.init();
}

void add(ll now,ll l,ll r,ll u,ll v)
{
    if(l==u&&v==r)
    {
        node[now].lazy=node[now].lazy*tmp;
        node[now].jz=node[now].jz*tmp;
        return;
    }
    down(now);
    Jz res;
    if(u<=mid)
    {
        add(node[now].ls,l,mid,u,min(v,mid));
    }
    if(mid<v)
    {
        add(node[now].rs,mid+1,r,max(mid+1,u),v);
    }
    if(l<=mid) res=res+node[node[now].ls].jz;
    if(mid<r) res=res+node[node[now].rs].jz;
    node[now].jz=res;
}

ll ask(ll now,ll l,ll r,ll u,ll v)
{
    if(l==u&&v==r)
    {
        return node[now].jz.num[1][0];
    }
    down(now);
    ll res=0;
    if(u<=mid)
    {
        res+=ask(node[now].ls,l,mid,u,min(mid,v));
    }
    if(mid<v)
    {
        res+=ask(node[now].rs,mid+1,r,max(u,mid+1),v);
    }
    return res%M;
}

int main()
{
    dw.num[0][0]=dw.num[0][1]=dw.num[1][0]=1;
    ll i,j,o,p,q,z;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&num[i]);
    }
    build(++tt,1,n);
    for(i=1;i<=tt;i++) node[i].lazy.init();
    for(i=1;i<=m;i++)
    {
        scanf("%lld",&o);
        if(o==1)
        {
            scanf("%lld%lld%lld",&p,&q,&z);
            tmp=dw.po(z);
            add(1,1,n,p,q);
        }
        else
        {
            scanf("%lld%lld",&p,&q);
            printf("%lld\n",ask(1,1,n,p,q));
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值