[XR-1]分块
[XR-1]分块
这题其实就是问把一段序列划分成若干个子段并限制子段大小的方案数.
首先,要把子段的大小集合搞出来.这其实就是求两个不可重集合的交.
可以直接用桶,我用了 \(set\) , 要稍微麻烦一些.
去重之后,就可以直接 \(Dp\) 了,令 \(f_n\) 表示长度为 \(n\) 的序列的方案数.
那么转移显然有:
\[f_n=\sum_{i=1}^m{[n\ge v_i]\times f_{n-v_i}}
\]
其中 , \(v_i\) 是子段大小集合, \(m\) 是子段大小的个数 , \([x]\) 当且仅当 \(x\) 表达式为真时为 \(1\) , 否则为 \(0\) .
这样的复杂度是 \(\Theta(n\times max\{v_i\})\) 的,足以通过 \(60\%\) 的数据.
那么对于 \(100\%\) 的数据该怎么去做呢?
我们发现 \(n\) 的范围高达 \(10^{18}\) , 能做到这样复杂度且被我们熟知的算法只有 \(\Theta(size^3\times log_2{n})\) 的矩乘.
并且我们发现,这个 \(DP\) 实际上就是至多有 \(100\) 个相关状态的递推.
所以完全可以尝试构造转移矩阵.
显然,这个转移矩阵应该是 \(100\times 100\) 的.
那么我们先确定初始矩阵是什么样子 \(:\)
\[\left[\begin{array}{llll} f_{size-1} & f_{size-2} & ... & f_{0}\end{array}\right]
\]
它非常的正常,除了有点长.
目标矩阵呢?
\[\left[\begin{array}{llll} f_{size} & f_{size} & ... & f_{1}\end{array}\right]
\]
转移矩阵呢?
\[\left[\begin{array}{llll} ? & ? & ... & ? & ? \\ ? & ? & ... & ? & ? \\ ? & ? & ... & ? & ? \\ &&...&& \\ ? & ? & ... & ? & ? \\ ? & ? & ... & ? & ? \\ ? & ? & ... & ? & ? \end{array}\right]
\]
\(100\times 100\) 的,因为你要保留所有状态,他们都可能是有用的.
他长什么样呢?
第一列中显然所有的 \(v_i\) 对应的位置都是 \(1\) , 其他列呢?
显然都可以从上一个矩阵直接抠下来,所以只有 \((i,i+1)\) 是 \(1\) .
然后转移就完了.
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <ctime>
#include <map>
#include <set>
#define MEM(x,y) memset ( x , y , sizeof ( x ) )
#define rep(i,a,b) for (int i = (a) ; i <= (b) ; ++ i)
#define per(i,a,b) for (int i = (a) ; i >= (b) ; -- i)
#define pii pair < int , int >
#define one first
#define two second
#define rint read<int>
#define int long long
#define pb push_back
#define db double
#define ull unsigned long long
#define lowbit(x) ( x & ( - x ) )
using std::queue ;
using std::set ;
using std::pair ;
using std::max ;
using std::min ;
using std::priority_queue ;
using std::vector ;
using std::swap ;
using std::sort ;
using std::unique ;
using std::greater ;
template < class T >
inline T read () {
T x = 0 , f = 1 ; char ch = getchar () ;
while ( ch < '0' || ch > '9' ) {
if ( ch == '-' ) f = - 1 ;
ch = getchar () ;
}
while ( ch >= '0' && ch <= '9' ) {
x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
ch = getchar () ;
}
return f * x ;
}
const int N = 1e6 + 100 ;
const int mod = 1e9 + 7 ;
set < int > rab , fis , tmp ; int maxv = - 1 ;
int f[N] , n , pr , nf , v[N] , cnt ;
struct Matrix {
int e[105][105] , line , row ;
inline void clear () { MEM ( e , 0 ) ; line = row = 0 ; return ; }
inline void init () { rep ( i , 1 , line ) e[i][i] = 1 ; }
friend Matrix operator * (Matrix a , Matrix b) {
Matrix res ; res.clear () ; res.line = a.line ; res.row = b.row ;
rep ( k , 1 , a.row ) rep ( i , 1 , a.line ) rep ( j , 1 , b.row )
res.e[i][j] = ( res.e[i][j] + a.e[i][k] * b.e[k][j] % mod ) % mod ;
return res ;
}
friend Matrix operator ^ (Matrix a , int p) {
Matrix res ; res.clear () ; res.line = res.row = a.line ; res.init () ;
while ( p ) {
if ( p & 1 ) res = res * a ;
a = a * a ; p >>= 1 ;
}
return res ;
}
} mat , ans ;
signed main (int argc , char * argv[]) {
n = rint () ;
pr = rint () ; rep ( i , 1 , pr ) rab.insert ( rint () ) ;
nf = rint () ; rep ( i , 1 , nf ) fis.insert ( rint () ) ;
for (auto it = rab.begin () ; it != rab.end () ; ++ it) {
int reg = *it ;
if ( fis.find ( reg ) == fis.end () ) continue ;
tmp.insert ( reg ) ;
}
for (auto it = tmp.begin () ; it != tmp.end () ; ++ it) v[++cnt] = *it ;
rep ( i , 1 , cnt ) f[v[i]] = 1 ;
rep ( i , 1 , 100 ) rep ( j , 1 , cnt ) {
if ( i - v[j] < 0 ) continue ;
f[i] = ( f[i] + f[i-v[j]] ) % mod ;
}
if ( n <= 100 ) return printf ("%lld\n" , f[n] ) , 0 ;
mat.clear () ; ans.clear () ;
rep ( i , 1 , cnt ) mat.e[v[i]][1] = 1 ;
rep ( i , 1 , 100 ) mat.e[i][i+1] = 1 ;
mat.line = mat.row = 100 ; ans.line = 1 ; ans.row = 100 ;
rep ( i , 1 , 100 ) ans.e[1][i] = f[100-i] ;
mat = mat ^ n ; ans = ans * mat ;
printf ("%lld\n" , ans.e[1][100] % mod ) ;
return 0 ;
}
May you return with a young heart after years of fighting.