河南省多校联盟二-F 线段树+矩阵

---恢复内容开始---

1284: SP教数学

时间限制: 2 秒  内存限制: 128 MB
提交: 24  解决: 4

题目描述

输入

输出

对于每组数据的2操作,输出一行对1e9 + 7取模的答案

样例输入

7 4
2 2 1 1 3 3 2
2 1 5
2 6 7
1 3 4 3
2 6 6

样例输出

6
3
2

提示

1 <=  n ,m <=10^5

一道很有趣的ST的题目,有趣在维护节点时用到了矩阵运算,减少了计算量。

首先对于矩阵运算法则,百度百科:

基本性质

  1. 乘法结合律: (AB)C=A(BC).[2] 
  2. 乘法左分配律:(A+B)C=AC+BC[2] 
  3. 乘法右分配律:C(A+B)=CA+CB[2] 
  4. 对数乘的结合性k(AB)=(kA)B=A(kB).
  5. 转置 (AB)T=BTAT
  6. 矩阵乘法一般不满足交换律[3] 

题目的重点是如何快速求出∑ri=l f(i) , 其中f(i)表示第i个斐波那契数,(f1=f2=1)

这里的i会更新,+x操作,那么如何快速的计算这个值呢,一项一项利用快速幂? len*log(n)的复杂度难免有些太高了。

我们可以利用当前已知的区间和和增量一次性计算得到新的区间和,这里用到一些公式。

假设当前区间有三个元素 [a,b,c] ,s1=fa+fb+fc  增量为x,

-->[a+x,b+x,c+x] ,  s2=f(a+x)+f(b+x),f(c+x);

由矩阵关于斐波那契的递推式有:(fn,fn-1)=(fn-1,fn-2)*(1 01 1)    //请自行脑补对齐= =

-->(f(a+x),f(a+x-1))=(f(a),f(a-1))*(1 01 1)^x

(f(b+x),f(b+x-1))=(f(b),f(b-1))*(1 01 1)^x

(f(c+x),f(c+x-1))=(f(c),f(c-1))*(1 01 1)^x

将三个等式相加得到 (s2,f(a+x-1)+f(b+x-1)+f(c+x-1))=(s1,f(a-1)+f(b-1)+f(c-1))*(1 01 1)^x  //由于矩阵运算满足单向的分配律

我们已经找到了s2和s1的关系了,在已知x和行矩阵的第二个元素的情况下,只需要对(1 01 1)进行一次矩阵幂便可得到s2.

由于计算时必须要知道行矩阵的第二项,我们不妨对于每个节点维护两个值,是Σri=l f(i) 和  Σri=l f(i-1),利用laz标记的x便可快速的完成释放操作。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define LL long long
  4 LL MOD=1e9+7;
  5 const LL MAXN=(100000<<2)+15;
  6 struct Matrix
  7 {
  8     int r=2,w=2;
  9     LL a[4][4];
 10     void init(){memset(a,0,sizeof(a));}
 11     Matrix operator*(const Matrix &tmp){
 12      Matrix ans;
 13      ans.r=r;ans.w=tmp.w;
 14      ans.init();
 15      for(int i=1;i<=r;++i)
 16         for(int k=1;k<=w;++k)
 17           for(int j=1;j<=tmp.w;++j)
 18     ans.a[i][j]=(ans.a[i][j]+a[i][k]*tmp.a[k][j])%MOD;
 19     return ans;
 20     }
 21     Matrix operator+(const Matrix &tmp){
 22      Matrix ans;
 23      ans.r=r;ans.w=w;
 24      ans.init();
 25      for(int i=1;i<=r;++i)
 26         for(int j=1;j<=w;++j)
 27     ans.a[i][j]=(ans.a[i][j]+a[i][j]+tmp.a[i][j])%MOD;
 28     return ans;
 29     }
 30 }unit,Am;
 31 void init()
 32 {
 33     unit.init();unit.r=unit.w=2;
 34     for(int i=0;i<=3;++i) unit.a[i][i]=1;
 35     Am.init(),Am.r=Am.w=2;
 36     Am.a[1][1]=Am.a[1][2]=Am.a[2][1]=1;
 37 }
 38 Matrix qpow(Matrix A,LL n)
 39 {
 40  Matrix ans=unit;
 41  ans.r=A.r;
 42  ans.w=A.w;
 43  while(n){
 44     if(n&1) ans=ans*A;
 45     A=A*A;
 46     n>>=1;
 47  }
 48  return ans;
 49 }
 50 struct node
 51 {
 52     LL x,y;
 53 };
 54 struct Segtree
 55 {
 56     #define M ((L+R)>>1)
 57     #define lc (id<<1)
 58     #define rc (id<<1|1)
 59     LL laz[MAXN];
 60     node sum[MAXN];
 61     void init()
 62     {
 63         memset(laz,0,sizeof(laz));
 64         memset(sum,0,sizeof(sum));
 65     }
 66     void build(int L,int R,int id)
 67     {
 68        if(L==R){
 69         LL x; scanf("%lld",&x);
 70         if(x==1){sum[id].x=1;}
 71         else if(x==2) {sum[id].x=sum[id].y=1;}
 72         else{
 73             Matrix t=qpow(Am,x-2);
 74             sum[id].x=(t.a[1][1]+t.a[1][2])%MOD;
 75             sum[id].y=(t.a[1][2]+t.a[2][2])%MOD;
 76            // cout<<sum[id].x<<" "<<sum[id].y<<endl;
 77         }
 78         return;
 79        }
 80        build(L,M,lc);
 81        build(M+1,R,rc);
 82        pushup(L,R,id);
 83     }
 84     void pushdown(int L,int R,int id)
 85     {
 86      if(!laz[id]) return;
 87      laz[lc]+=laz[id];
 88      laz[rc]+=laz[id];
 89      Matrix t=qpow(Am,laz[id]);
 90      LL x1=sum[lc].x,y1=sum[lc].y;
 91      sum[lc].x=(x1*t.a[1][1]%MOD+y1*t.a[2][1]%MOD)%MOD;
 92      sum[lc].y=(x1*t.a[1][2]%MOD+y1*t.a[2][2]%MOD)%MOD;
 93       x1=sum[rc].x,y1=sum[rc].y;
 94      sum[rc].x=(x1*t.a[1][1]%MOD+y1*t.a[2][1]%MOD)%MOD;
 95      sum[rc].y=(x1*t.a[1][2]%MOD+y1*t.a[2][2]%MOD)%MOD;
 96      laz[id]=0;
 97     }
 98     void pushup(int L,int R,int id)
 99     {
100      sum[id].x=(sum[lc].x+sum[rc].x)%MOD;
101      sum[id].y=(sum[lc].y+sum[rc].y)%MOD;
102     }
103     void update(int L,int R,int id,int l,int r,LL v)
104     {
105      if(L>=l&&R<=r){
106         laz[id]+=v;
107         Matrix t=qpow(Am,v);
108         LL x1=sum[id].x,y1=sum[id].y;
109         sum[id].x=(x1*t.a[1][1]%MOD+y1*t.a[2][1]%MOD)%MOD;
110         sum[id].y=(x1*t.a[1][2]%MOD+y1*t.a[2][2]%MOD)%MOD;
111         return;
112      }
113      pushdown(L,R,id);
114      if(l<=M) update(L,M,lc,l,r,v);
115      if(r>M) update(M+1,R,rc,l,r,v);
116      pushup(L,R,id);
117     }
118     LL ask(int L,int R,int id,int l,int r)
119     {
120      if(L>=l&&R<=r) return sum[id].x%MOD;
121      pushdown(L,R,id);
122      LL s=0;
123      if(l<=M) s=(s+ask(L,M,lc,l,r))%MOD;
124      if(r>M) s=(s+ask(M+1,R,rc,l,r))%MOD;
125      pushup(L,R,id);
126      return s;
127     }
128 }seg;
129 int main()
130 {
131     init();
132     LL n,m,x,a;
133     int i,opt,j,l,r,k;
134     while(cin>>n>>m){
135         seg.init();
136         seg.build(1,n,1);
137         for(i=1;i<=m;++i){
138             cin>>opt;
139             LL v;
140             if(opt==1){
141                 cin>>l>>r>>v;
142                 seg.update(1,n,1,l,r,v);
143             }
144             else{
145                 cin>>l>>r;
146                 cout<<seg.ask(1,n,1,l,r)<<endl;
147             }
148         }
149     }
150     return 0;
151 }
152  

1284: SP教数学

时间限制: 2 秒  内存限制: 128 MB
提交: 24  解决: 4

题目描述

输入

---恢复内容结束---

时间: 2024-10-29 19:09:44

河南省多校联盟二-F 线段树+矩阵的相关文章

河南省多校联盟二-A

1279: 简单的背包问题 时间限制: 1 秒  内存限制: 32 MB提交: 361  解决: 20 题目描述 相信大家都学过背包问题了吧,那么现在我就考大家一个问题.有n个物品,每个物品有它的重量w,价值v,现在有一个容量为W的背包,问你在不超过背包容量的情况下,能装下的物品的最大价值是多少. T <= 100代表样例数 1 <= n <= 100 物品数 1 <= W <= 100000 背包的容量 1 <= wi <= 100000 每个物品的重量 对于每

河南省多校联盟二-C

1281: 邪能炸弹 时间限制: 1 秒  内存限制: 128 MB提交: 222  解决: 80 题目描述 正在入侵艾泽拉斯的古尔丹偶然间得到了一颗邪能炸弹,经过研究,他发现这是一颗威力极其巨大且难以控制的炸弹.但是精通邪能的古尔丹突然有了一个大胆的想法,他对炸弹进行了一些小小的改造.这使得炸弹需要n天的充能才能爆炸,在这n天中,每天炸弹的邪能值都会产生波动,波动值为xi,古尔丹唯一能控制的是使邪能值增加xi或减少xi,如果邪能值小于0或大于MAX,那么炸弹将会损坏并失效.机智如古尔丹当然会做

UVA 11297 Census ——二维线段树

[题目分析] 二维线段树模板题目. 简直就是无比的暴力.时间复杂度为两个log. 标记的更新方式比较奇特,空间复杂度为N^2. 模板题目. [代码] #include <cstdio> #include <cstring> //#include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue> #include <str

【BZOJ1513】[POI2006]Tet-Tetris 3D 二维线段树

[BZOJ1513][POI2006]Tet-Tetris 3D Description Task: Tetris 3D "Tetris" 游戏的作者决定做一个新的游戏, 一个三维的版本, 在里面很多立方体落在平面板,一个立方体开始落下直到碰上一个以前落下的立方体或者落地即停止. 作者想改变一下游戏的目的使得它更大众化,在新游戏中你将知道落下的立方体信息以及位置,你的任务就是回答所有立方体落下后最高的方块的高度.所有的立方体在下落过程中都是垂直的并且不会旋转.平板左下角坐标为原点,并且

POJ2155 Matrix二维线段树经典题

题目链接 二维树状数组 1 #include<iostream> 2 #include<math.h> 3 #include<algorithm> 4 #include<stdlib.h> 5 using namespace std; 6 #define ll long long 7 #define re(i,n) for(int i=0;i<n;i++) 8 const int maxn = 1007; 9 int c[maxn][maxn]; 10

Luck and Love(二维线段树)

Luck and Love Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 54 Accepted Submission(s): 21   Problem Description 世界上上最远的距离不是相隔天涯海角而是我在你面前可你却不知道我爱你                ―― 张小娴 前段日子,枫冰叶子给Wiskey做了个征婚启事,聘

Luck and Love (二维线段树)(树套树)

Luck and Love Problem Description 世界上上最远的距离不是相隔天涯海角 而是我在你面前 可你却不知道我爱你 ―― 李丹妮 前段日子,枫冰叶子给Wiskey做了个征婚启事,聘礼达到500万哦,天哪,可是天文数字了啊,不知多少MM蜂拥而至,顿时万人空巷,连扫地的大妈都来凑热闹来了.―_―||| 由于人数太多,Wiskey实在忙不过来,就把统计的事情全交给了枫冰叶子,自己跑回家休息去了.这可够枫冰叶子忙的了,他要处理的有两类事情,一是得接受MM的报名,二是要帮Wiske

浅谈二维线段树

一.定义 二维线段树,即用线段树维护一个矩阵 有两种实现方式: 1.原一维线段树的基础上,每一个节点都是一个线段树,代表第二维 下图是一个4*4矩阵 2.四分法转化为一维线段树 两种方法的空间复杂度都是n*n*log^2 第一种方法单次操作的时间复杂度是log^2,第二种方法最差可以退化到n 一维线段树的标记思想,在第一种方法中,可以用于二维线段树的第二维,不可以用于二维线段树的第一维 第二种方法本质上是四叉的一维线段树, 在此只介绍第一种方法 二.基本操作 1.单点修改+矩阵查询 单次访问一个

[JZOJ3615]【NOI2014模拟】数列(平面几何+二维线段树)

Description 给定一个长度为n的正整数数列a[i]. 定义2个位置的f值为两者位置差与数值差的和,即f(x,y)=|x-y|+|a[x]-a[y]|. 你需要写一个程序支持2种操作(k都是正整数): Modify x k:将第x个数的值修改为k. Query x k:询问有几个i满足f(x,i)<=k.询问不仅要考虑当前数列,还要考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的f值<=k的对数.(某位置多次修改为同样的数值,按多次统计) Main 令F(x,y)=