CF821 E. Okabe and El Psy Kongroo 矩阵快速幂
题意:给出$n$条平行于x轴的线段,终点$k$坐标$(k <= 10^{18})$,现在可以在线段之间进行移动,但不能超出两条线段的y坐标所夹范围,问到达终点有几种方案。
思路:刚开始以为限制只是到达线段上就必须沿线段走,后来才发现是要求走y坐标所夹范围,那么就简单多了,很容易看出是个递推形DP,然而数据量有点大,k为10的18次,一般转移显然不可行。由于是个递推,而且y坐标最大也只有15,故使用矩阵优化递推复杂度即可。
/** @Date : 2017-07-04 16:06:18 * @FileName: E 矩阵快速幂 + 递推.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; const LL mod = 1e9 + 7; int len; LL n, k; struct yuu { LL mat[18][18]; yuu(){MMF(mat);} void init() { for(int i = 0; i <= 17; i++) mat[i][i] = 1; } yuu operator *(const yuu &b) { yuu c; for(int i = 0; i <= len; i++) { for(int j = 0; j <= len; j++) { for(int k = 0; k <= len; k++) { c.mat[i][j] = (c.mat[i][j] + this->mat[i][k] * b.mat[k][j] % mod) % mod; } } } return c; } }; yuu fpow(yuu a, LL n) { yuu res; res.init(); while(n) { if(n & 1) res = res * a; a = a * a; n >>= 1; } return res; } int main() { while(cin >> n >> k) { yuu A, B, t; for(int i = 0; i < 16; i++) { int x = i - 1; if(x < 0) A.mat[i][x + 1] = 1; else A.mat[i][x] = 1; A.mat[i][x + 1] = A.mat[i][x + 2] = 1; } t.mat[0][0] = 1; int flag = 0; for(int i = 0; i < n; i++) { LL l, r, c; scanf("%lld%lld%lld", &l, &r, &c); if(flag) continue; flag = 0; r = min(r, k); if(r == k) flag = 1; len = c; B = fpow(A, r - l); for(int i = c + 1; i < 16; i++) t.mat[i][0] = 0; B = B * t; for(int i = 0; i <= len; i++) t.mat[i][0] = B.mat[i][0]; } printf("%lld\n", B.mat[0][0]); } return 0; }