题目链接:http://codeforces.com/problemset/problem/1152/F2
题目大意
见http://codeforces.com/problemset/problem/1152/F1,此题 n 最大能到 109。
分析
在 F1 的基础上,我们发现 dp[i + 1] 数组的每个值均可以通过 dp[i] 数组的有限几个数求得,而 dp[i + 2] 数组的每个值也均可以通过 dp[i + 1] 数组相同位置的有限几个数求得,于是我们可以考虑用矩阵快速幂来求 dp[n]。
由于 dp 数组的第二维和第三维数值并不是特别大,我们可以把 dp 数组的后两个维度压制成一个维度,于是 dp[i][j][sta] 就变成 dp[i][cur],其中 cur = j * (1 << m) + sta。
假设 dp[i][cur] 对某一个 dp[i + 1][newcur] 有贡献,根据上一题的帖子,有两种可能:
- 不访问:dp[i + 1][newcur] += dp[i][cur]。
- 访问:dp[i + 1][newcur] += dp[i][cur] * (1 + 后m个星球被访问过的星球个数)。
定义 base 矩阵是我们所要构造的矩阵,设 base[cur][newcur] 代表某一个状态 cur 对一个新状态 newcur 所做贡献的系数,于是有:
- 不访问:base[cur][newcur] = 1。
- 访问:base[cur][newcur] = (1 + 后m个星球被访问过的星球个数)。
- 其他(cur 压根不可能变成 newcur):base[cur][newcur] = 0。
于是 $dp[i + 1][newcur] = sum_{cur = 0}^{maxCur} dp[i][cur] * base[cur][newcur]$。
根据这个式子可以构造 dp 矩阵如下(以 k = 1, m = 1 为例):
$$
dp(i) = egin{bmatrix}
dp[i][0] & dp[i][1] & dp[i][2] & dp[i][3]
end{bmatrix}
$$
dp(i) = egin{bmatrix}
dp[i][0] & dp[i][1] & dp[i][2] & dp[i][3]
end{bmatrix}
$$
相应 base 系数矩阵如下:
$$
base = egin{bmatrix}
base[0][0] & base[0][1] & base[0][2] & base[0][3] \
base[1][0] & base[1][1] & base[1][2] & base[1][3] \
base[2][0] & base[2][1] & base[2][2] & base[2][3] \
base[3][0] & base[3][1] & base[3][2] & base[3][3]
end{bmatrix}
$$
base 矩阵填上数后为:
$$
base = egin{bmatrix}
1 & 1 & 0 & 0 \
0 & 0 & 0 & 0 \
0 & 0 & 1 & 1 \
1 & 2 & 0 & 0
end{bmatrix}
$$
于是有:
$$
egin{align*}
dp(i) &= dp(i - 1) * base \
dp(n) &= dp(0) * base^{n}
end{align*}
$$
不知道关于矩阵怎么构造的说的明不明白,有不足的地方还请指出。
PS:如果不压制维度,那么 base 矩阵就要弄四维的了,四维矩阵乘法反正我是不会。
代码如下
时间复杂度:$O(log n*(k*2^m)^3)$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INIT() ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 5 #define Rep(i,n) for (int i = 0; i < (n); ++i) 6 #define For(i,s,t) for (int i = (s); i <= (t); ++i) 7 #define rFor(i,t,s) for (int i = (t); i >= (s); --i) 8 #define ForLL(i, s, t) for (LL i = LL(s); i <= LL(t); ++i) 9 #define rForLL(i, t, s) for (LL i = LL(t); i >= LL(s); --i) 10 #define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 11 #define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i) 12 13 #define pr(x) cout << #x << " = " << x << " " 14 #define prln(x) cout << #x << " = " << x << endl 15 16 #define LOWBIT(x) ((x)&(-x)) 17 18 #define ALL(x) x.begin(),x.end() 19 #define INS(x) inserter(x,x.begin()) 20 21 #define ms0(a) memset(a,0,sizeof(a)) 22 #define msI(a) memset(a,inf,sizeof(a)) 23 #define msM(a) memset(a,-1,sizeof(a)) 24 25 #define MP make_pair 26 #define PB push_back 27 #define ft first 28 #define sd second 29 30 template<typename T1, typename T2> 31 istream &operator>>(istream &in, pair<T1, T2> &p) { 32 in >> p.first >> p.second; 33 return in; 34 } 35 36 template<typename T> 37 istream &operator>>(istream &in, vector<T> &v) { 38 for (auto &x: v) 39 in >> x; 40 return in; 41 } 42 43 template<typename T1, typename T2> 44 ostream &operator<<(ostream &out, const std::pair<T1, T2> &p) { 45 out << "[" << p.first << ", " << p.second << "]" << " "; 46 return out; 47 } 48 49 inline int gc(){ 50 static const int BUF = 1e7; 51 static char buf[BUF], *bg = buf + BUF, *ed = bg; 52 53 if(bg == ed) fread(bg = buf, 1, BUF, stdin); 54 return *bg++; 55 } 56 57 inline int ri(){ 58 int x = 0, f = 1, c = gc(); 59 for(; c<48||c>57; f = c=='-'?-1:f, c=gc()); 60 for(; c>47&&c<58; x = x*10 + c - 48, c=gc()); 61 return x*f; 62 } 63 64 typedef long long LL; 65 typedef unsigned long long uLL; 66 typedef pair< double, double > PDD; 67 typedef pair< int, int > PII; 68 typedef pair< string, int > PSI; 69 typedef set< int > SI; 70 typedef vector< int > VI; 71 typedef map< int, int > MII; 72 typedef pair< LL, LL > PLL; 73 typedef vector< LL > VL; 74 typedef vector< VL > VVL; 75 const double EPS = 1e-10; 76 const LL inf = 0x7fffffff; 77 const LL infLL = 0x7fffffffffffffffLL; 78 const LL mod = 1e9 + 7; 79 const int maxN = 1e5 + 7; 80 const LL ONE = 1; 81 const LL evenBits = 0xaaaaaaaaaaaaaaaa; 82 const LL oddBits = 0x5555555555555555; 83 84 void add_mod(LL &a, LL b) { 85 a = (a + b) % mod; 86 } 87 88 struct Matrix{ 89 int row, col; 90 LL MOD; 91 VVL mat; 92 93 Matrix(int r, int c, LL p = mod) : row(r), col(c), MOD(p) { 94 mat.assign(r, VL(c, 0)); 95 } 96 Matrix(const Matrix &x, LL p = mod) : MOD(p){ 97 mat = x.mat; 98 row = x.row; 99 col = x.col; 100 } 101 Matrix(const VVL &A, LL p = mod) : MOD(p){ 102 mat = A; 103 row = A.size(); 104 col = A[0].size(); 105 } 106 107 // x * 单位阵 108 inline void E(int x = 1) { 109 assert(row == col); 110 Rep(i, row) mat[i][i] = x; 111 } 112 113 inline VL& operator[] (int x) { 114 assert(x >= 0 && x < row); 115 return mat[x]; 116 } 117 118 inline Matrix operator= (const VVL &x) { 119 row = x.size(); 120 col = x[0].size(); 121 mat = x; 122 return *this; 123 } 124 125 inline Matrix operator+ (const Matrix &x) { 126 assert(row == x.row && col == x.col); 127 Matrix ret(row, col); 128 Rep(i, row) { 129 Rep(j, col) { 130 ret.mat[i][j] = mat[i][j] + x.mat[i][j]; 131 ret.mat[i][j] %= MOD; 132 } 133 } 134 return ret; 135 } 136 137 inline Matrix operator* (const Matrix &x) { 138 assert(col == x.row); 139 Matrix ret(row, x.col); 140 Rep(k, x.col) { 141 Rep(i, row) { 142 if(mat[i][k] == 0) continue; 143 Rep(j, x.col) { 144 ret.mat[i][j] += mat[i][k] * x.mat[k][j]; 145 ret.mat[i][j] %= MOD; 146 } 147 } 148 } 149 return ret; 150 } 151 152 inline Matrix operator*= (const Matrix &x) { return *this = *this * x; } 153 inline Matrix operator+= (const Matrix &x) { return *this = *this + x; } 154 155 inline void print() { 156 Rep(i, row) { 157 Rep(j, col) { 158 cout << mat[i][j] << " "; 159 } 160 cout << endl; 161 } 162 } 163 }; 164 165 // 矩阵快速幂,计算x^y 166 inline Matrix mat_pow_mod(Matrix x, LL y) { 167 Matrix ret(x.row, x.col); 168 ret.E(); 169 while(y){ 170 if(y & 1) ret *= x; 171 x *= x; 172 y >>= 1; 173 } 174 return ret; 175 } 176 177 int n, k, m, maxSta; 178 LL ans; 179 180 int toId(int j, int sta) { 181 return j * maxSta + sta; 182 } 183 184 int main(){ 185 INIT(); 186 cin >> n >> k >> m; 187 maxSta = 1 << m; 188 Matrix dp(1, (k + 1) * maxSta); 189 dp.mat[0][0] = 1; 190 Matrix base((k + 1) * maxSta, (k + 1) * maxSta); 191 192 // 构造base矩阵 193 Rep(j, k + 1) { 194 Rep(sta, maxSta) { 195 int newsta = (sta << 1) % maxSta; 196 int cur = toId(j, sta), newcur = toId(j, newsta); 197 198 // 不选 i + 1 个星球从cur->newcur的状态变化 199 base.mat[cur][newcur] = 1; 200 // 选 i + 1 个星球从cur->newcur的状态变化 201 if (j < k) { 202 newcur = toId(j + 1, newsta | 1); 203 base.mat[cur][newcur] = __builtin_popcount(sta) + 1; 204 } 205 } 206 } 207 208 base = mat_pow_mod(base, n); 209 dp *= base; 210 211 Rep(sta, maxSta) add_mod(ans, dp.mat[0][toId(k, sta)]); 212 cout << ans << endl; 213 return 0; 214 }