zoj3772【线段树+矩阵相乘】

Calculate the Function


Time Limit: 2 Seconds      Memory Limit: 65536 KB


You are given a list of numbers A1 A2 .. AN and M queries. For the i-th query:

  • The query has two parameters Li and Ri.
  • The query will define a function Fi(x) on the domain [Li, Ri] ∈ Z.
  • Fi(Li) = ALi
  • Fi(Li + 1) = A(Li + 1)
  • for all x >= Li + 2Fi(x) = Fi(x - 1) + Fi(x - 2) × Ax

You task is to calculate Fi(Ri) for each query. Because the answer can be very large, you should output the remainder of the answer divided by 1000000007.

Input

There are multiple test cases. The first line of input is an integer T indicates the number of test cases. For each test case:

The first line contains two integers NM (1 <= NM <= 100000). The second line contains N integers A1 A2 .. AN (1 <= Ai <= 1000000000).

The next M lines, each line is a query with two integer parameters LiRi (1 <= Li <= Ri <= N).

Output

For each test case, output the remainder of the answer divided by 1000000007.

Sample Input

1
4 7
1 2 3 4
1 1
1 2
1 3
1 4
2 4
3 4
4 4

Sample Output

1
2
5
13
11
4
4

题目大意很简单:就告诉你一个公式然后进行询问

分析:

这是昨天训练赛的题目

比赛的时候还没看

昨晚我以为是推公式进行求解

半天推出来的公式是错的TAT

其实我应该想到的

数据范围10^5若是公式的话不应该这么小的范围

这个的复杂度应该是一个nlog(n)的级别

后来周洲他们队结束之后a了

我打开第一眼

线段树

那时候脑袋里突然就想到了大概的方向

其实很简单的

F1, F2  可以推得 F2, F3

我们可以找到这么一个矩阵使其完成那个功能

这个矩阵我得到的是

1      1

a[3]  0

那么继续往下推

就是继续乘以矩阵

1      1

a[3]  0

每次都乘会超时

那么我们用线段树在存储就可以了

每个节点存的是该节点下的矩阵的子矩阵的乘积

最后查询的时候只要把它和F1, F2相乘就可以了

代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5
 6 const long long maxn = 100005;
 7 const long long mod = 1000000007;
 8 long long val[maxn];
 9 long long start[1][2];
10 long long end[1][2];
11
12 struct Node {
13     long long b[2][2];
14 };
15 struct Tree {
16     long long left, right;
17     Node node;
18 }tree[maxn << 2];
19
20 Node mul(Node n1, Node n2) {
21     Node n3;
22     for(long long i = 0; i < 2; i++) {
23         for(long long j = 0; j < 2; j++) {
24             n3.b[i][j] = 0;
25             for(long long k = 0; k < 2; k++) {
26                 n3.b[i][j] += n1.b[i][k] * n2.b[k][j];
27                 n3.b[i][j] %= mod;
28             }
29         }
30     }
31     return n3;
32 }
33
34 Node Build(long long root, long long left, long long right) {
35     tree[root].left = left; tree[root].right = right;
36     if(left == right) {
37         tree[root].node.b[0][0] = 1; tree[root].node.b[0][1] = 1;
38         tree[root].node.b[1][0] = val[left]; tree[root].node.b[1][1] = 0;
39         return tree[root].node;
40     }
41     long long mid = ( left + right ) >> 1;
42     return tree[root].node = mul(Build(root << 1, left, mid), Build(root << 1 | 1, mid + 1, right));
43 }
44
45 Node x;
46 void init() {
47     x.b[0][0] = 1; x.b[0][1] = 0;
48     x.b[1][0] = 0; x.b[1][1] = 1;
49 }
50
51 Node cal(long long root, long long left, long long right) {
52     if(tree[root].left > right || tree[root].right < left) {
53         return x;
54     }
55     if(left <= tree[root].left && tree[root].right <= right) {
56         return tree[root].node;
57     }
58     return mul(cal(root << 1, left, right), cal(root << 1 | 1, left, right) );
59 }
60
61 int main() {
62     long long t, n, m;
63     long long a, b;
64     init();
65     scanf("%lld",&t);
66     while(t--) {
67         scanf("%lld %lld",&n, &m);
68         for(long long i = 1; i <= n; i++) {
69             scanf("%lld",&val[i]);
70         }
71         Build(1, 1, n);
72         while(m--) {
73             scanf("%lld %lld",&a, &b);
74             if(b <= a + 1) {
75                 printf("%lld\n",val[b]);
76                 continue;
77             }
78             start[0][0] = val[a + 1]; start[0][1] = val[a];
79             Node mid = cal(1, a + 2, b);
80             for(long long i = 0; i < 1; i++) {
81                 for(long long j = 0; j < 2; j++) {
82                     end[i][j] = 0;
83                     for(long long k = 0; k < 2; k++) {
84                         end[i][j] += start[i][k] * mid.b[k][j];
85                         end[i][j] %= mod;
86                     }
87                 }
88             }
89             printf("%lld\n", end[0][0]);
90         }
91     }
92     return 0;
93 }

时间: 2024-11-08 21:33:47

zoj3772【线段树+矩阵相乘】的相关文章

ZOJ 3772 Calculate the Function 线段树+矩阵

Calculate the FunctionTime Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit Status Appoint description:  System Crawler  (2014-04-09) Description You are given a list of numbers A1A2 .. AN and M queries. For the i-th query

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

Calculate the Function Problem's Link:   http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3772 Mean: 略 analyse: 简单的线段树维护矩阵. 矩阵乘法的结合律(a * b * c == a * (b * c)),注意矩阵乘法不满足分配率(a *b != b * a). 令 M[x] = [1 A[x]]              [1     0 ] ,那么有 [ F

hdu 5068(线段树+矩阵乘法)

矩阵乘法来进行所有路径的运算, 线段树来查询修改. 关键还是矩阵乘法的结合律. Harry And Math Teacher Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 326    Accepted Submission(s): 89 Problem Description As we all know, Harry Porter

CF719E(线段树+矩阵快速幂)

题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这个数组 因为矩阵的可加性,所以可以进行lazy操作 我最开始的想法是每个节点lazy表示该区间下标加了多少,add表示该区间已经加的下标对应的矩阵乘积,这样更新lazy是O(1)的,算add是O(logn)的 但是这样每次pushdown的时候,add下传总要多个log,会TLE 更好的办法是laz

HDU 5068 Harry And Math Teacher 线段树+矩阵乘法

题意: 一栋楼有n层,每一层有2个门,每层的两个门和下一层之间的两个门之间各有一条路(共4条). 有两种操作: 0 x y : 输出第x层到第y层的路径数量. 1 x y z : 改变第x层 的 y门 到第x+1层的 z门的通断情况. 思路: 门之间的路径数可以用矩阵来表示,经过的中间层可以用矩阵乘积表示. 所以用线段树维护矩阵乘积即可. 代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring>

zoj 3772 Calculate the Function(线段树+矩阵乘法)

Calculate the Function Time Limit: 2 Seconds      Memory Limit: 65536 KB You are given a list of numbers A1 A2 .. AN and M queries. For the i-th query: The query has two parameters Li and Ri. The query will define a function Fi(x) on the domain [Li,

Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树 矩阵面积并

D. Vika and Segments Vika has an infinite sheet of squared paper. Initially all squares are white. She introduced a two-dimensional coordinate system on this sheet and drew n black horizontal and vertical segments parallel to the coordinate axes. All

[HDU6155]Subsequence Count(线段树+矩阵)

DP式很容易得到,发现是线性递推形式,于是可以矩阵加速.又由于是区间形式,所以用线段树维护. https://www.cnblogs.com/Miracevin/p/9124511.html 关键在于证明区间操作中,可以直接在打标记的位置翻转矩阵两行两列. 上面网址用代数形式证了一遍,这里考虑从矩阵本身解释. 由线代内容可知,将一个矩阵作初等行变换,相当于将其左乘一个作了相应初等列变换的单位矩阵.同理将一个矩阵作初等列变换,相当于将其又乘一个作了相应初等行变换的单位矩阵. 这里,左乘的矩阵$T=

New Year and Old Subsequence CodeForces - 750E(线段树 + 矩阵)

New Year and Old Subsequence (CodeForces - 750E) 题意: 给出一个长度为\(N\)的数字串,\(q\)次询问.每次询问一段区间.在区间内删除尽量少的字符,使得区间内含有序列"\(2017\)",且不含有"\(2016\)". \(n,q<=200000\). 题解: 用\(01234\)五种状态分别表示"". "\(2\)"."\(20\)"."