ZOJ 3772 Calculate the Function 解题报告
来源:互联网 发布:丽江 大理 知乎 编辑:程序博客网 时间:2024/06/06 07:39
原题:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5235
题意:给定一个序列A,每次询问一组 L, R, 设 F(L) = AL, F(L+1) = AL+1, F(x) = F(x-1) + F(x-2) * Ax, 求F(R)
解法1:线段树
对于形如f(x) = p * f(x-1) + q * f(x-2)的通项都可以转成矩阵乘法,对于本题就是
于是依次拓展后得到
对于线段树的每个结点保存一个矩阵,如果结点是叶子,则有 a[0][0] = a[1][0] = 1, a[0][1] = Ax, a[1][1] = 0的形式
否则保存 a[r] * a[r-1] * ... * a[l] 的结果矩阵,要注意不要乘反了
于是对于每一个询问 l, r, 答案就是 find(l + 2, r) 的结果矩阵,再去乘 Al+1 Al 矩阵的结果
代码:
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <string>#include <vector>#include <map>#include <cmath>using namespace std;#define LL long long#define ULL unsigned long long#define mod 1000000007#define eps 1e-8#define MP make_pair#define mxn 100005LL A[mxn];int ll[mxn << 2], rr[mxn << 2];struct Matrix { LL a[2][2]; Matrix(){} Matrix( int x ){ a[0][0] = a[1][0] = 1; a[0][1] = x; a[1][1] = 0; }}M[mxn << 2];Matrix mul(Matrix A, Matrix B) { Matrix C; memset(C.a, 0, sizeof(C.a)); for( int i = 0; i < 2; ++i ) for( int j = 0; j < 2; ++j ) for( int k = 0; k < 2; ++k ) C.a[i][j] = (C.a[i][j] + A.a[i][k] * B.a[k][j]) % mod; return C;}void build( int l, int r, int i ) { ll[i] = l, rr[i] = r; if( l == r ) { M[i] = Matrix(A[l]); return ; } int m = (ll[i] + rr[i]) >> 1, ls = i << 1, rs = ls | 1; build(l, m, ls), build(m + 1, r, rs); M[i] = mul(M[rs], M[ls]);}Matrix find( int l, int r, int i ) { if( ll[i] == l && rr[i] == r ) return M[i]; int m = (ll[i] + rr[i]) >> 1, ls = i << 1, rs = ls | 1; if( r <= m ) return find(l, r, ls); if( l > m ) return find(l, r, rs); return mul(find(m + 1, r, rs), find(l, m, ls));}int main(){ //ios_base::sync_with_stdio(false); int t, n, m, l, r; cin >> t; while( t-- ) { cin >> n >> m; for( int i = 1; i <= n; ++i ) scanf( "%lld", A + i ); build(1, n, 1); while( m-- ) { scanf( "%d%d", &l, &r ); if( r - l <= 1 ) printf( "%lld\n", A[r] ); else { Matrix C = find(l + 2, r, 1); LL ans = C.a[0][0] * A[l+1] % mod + C.a[0][1] * A[l] % mod; printf( "%lld\n", ans % mod ); } } } return 0;}
解法2:分块
比赛的时候智商结石了,可耻的分块了:D
假设序列从F1开始,由于F(x) = F(x-1) + F(x-2) * Ax ,可知 F1 和 F2 永远不会乘在同一个多项式内
故设 F(x) = B(x) + C(x), 其中B(x)为含有F1的多项式之和,C(x)为含有F2的多项式之和
于是我们把序列分成长为300的段,每段令F1 = F2 = 1,序列的第1个数做为F3,依次推到后面
那么对于一组确定的F1, F2, 我们有 F(x) = B(x) * F1 + C(x) * F2
当询问区间长度小于300时,可以直接撸,大于300时,先找到第一个大于l + 1的段,然后从Fl推到这个段的前面
由于我们把每段的头做为F3, 所以上一段的最后两个数,就是这一段的 F1, F2,代入即可
代码:
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <string>#include <vector>#include <map>#include <cmath>using namespace std;#define LL long long#define ULL unsigned long long#define mod 1000000007#define eps 1e-8#define MP make_pair#define mxn 110005LL A[mxn], B[mxn], C[mxn], F[mxn];void init( int n ) { for( int i = 1; i <= n; i += 300 ) { B[i] = A[i]; C[i] = 1; B[i+1] = A[i]; C[i+1] = A[i+1] + 1; for( int j = i + 2; j < i + 300; ++j ) { B[j] = (B[j-1] + B[j-2] * A[j]) % mod; C[j] = (C[j-1] + C[j-2] * A[j]) % mod; } }}int main(){ //ios_base::sync_with_stdio(false); int t, n, m, l, r; cin >> t; while( t-- ) { cin >> n >> m; for( int i = 1; i <= n; ++i ) scanf( "%lld", A + i ); init(n); while( m-- ) { scanf( "%d%d", &l, &r ); if( r - l <= 350 ) { F[l] = A[l]; F[l+1] = A[l+1]; for( int i = l + 2; i <= r; ++i ) F[i] = (F[i-1] + F[i-2] * A[i]) % mod; printf( "%lld\n", F[r] ); continue; } int k = 1; while( k <= l + 1 ) k += 300; F[l] = A[l]; F[l+1] = A[l+1]; for( int i = l + 2; i < k; ++i ) F[i] = (F[i-1] + F[i-2] * A[i]) % mod; for( int i = k, j; ; i = j ) { j = i + 300; if( j <= r ) { F[j-2] = (B[j-2] * F[i-2] % mod + C[j-2] * F[i-1] % mod) % mod; F[j-1] = (B[j-1] * F[i-2] % mod + C[j-1] * F[i-1] % mod) % mod; } else { F[r] = (B[r] * F[i-2] % mod + C[r] * F[i-1] % mod) % mod; printf( "%lld\n", F[r] ); break; } } } } return 0;}
- ZOJ 3772 Calculate the Function 解题报告
- ZOJ 3772 Calculate the Function
- ZOJ 3772 Calculate the Function
- zoj 3772 Calculate the Function
- ZOJ 3772 Calculate the Function( 线段树 )
- zoj 3772 Calculate the Function(矩阵乘法)
- ZOJ 3772Calculate the Function(线段树)
- ZOJ 3772 —— Calculate the Function(矩阵乘法)
- zoj 3772 Calculate the Function monthly 14th
- zoj-3772-Calculate the Function-线段树+矩阵
- ZOJ Problem Set - 3772 Calculate the Function 矩阵+线段树
- zoj 3772 Calculate the Function 线段树+矩阵
- ZOJ 3772 Calculate the Function(线段树 + 矩阵)
- ZOJ 3772 Calculate the Function (线段树 + 矩阵)
- [矩阵+线段树] zoj 3772 Calculate the Function
- zoj 3772 Calculate the Function 矩阵相乘+ 线段树查询
- zoj 3772 Calculate the Function(矩阵+线段树)
- zoj 3772 Calculate the Function(线段树+矩阵乘法)
- 个人主页|我的博客
- guegvkreguegvkre
- Android表格布局的使用
- 最大化平均值
- cocos2dx学习之javascript(0.2)python建立javascript工程
- ZOJ 3772 Calculate the Function 解题报告
- 求区间内随机值
- Android 如何隐藏应用程序的图标
- 安卓JSON解析初步探讨
- webservice解释
- Android MediaRecorder视频录制,多分辨率调节
- 用标签写登录界面
- CH341SER_WIN7_X64 USB转串口驱动程序
- HDU 4333 Revolving Digits(KMP:循环节+扩展KMP)